C++ 类的多态一(virtual关键字--构造函数深刻理解)

//virtual关键字--构造函数深刻理解
#include<iostream>
using namespace std;

/*
C语言编译器,c++编译器全部是静态链编,就是一段一段代码的编译,不会考虑上下文
例如编译到void ProtectA(Point pin)   编译器就会认为pin就是Point类型,
而不会去联系上下文,去分析pin有没有可能是子类对象
virtual关键字会自动检测这个父类对象有没有是子类对象
*/

/*
virtual关键字的作用
假设父类中存在用virtual关键字修饰的函数A,在子类中重写了该函数
当变量是父类时,c++编译器会检测传递过来的变量是不是子类对象,
如果传过来的是子类对象,那么c++编译器会自动调用子类的同名函数A,
如果传过来的是父类对象,那么c++编译就无法访问子类的同名函数A,只能调用父类的函数A
*/

class Point{
public:
    Point(Point &p){
        cout << "我是父类,拷贝构造函数被执行了1" << endl;
        //this=   报错   这里发现  this的类型是 Point *const this  说明在执行构造函数之前就已经被分配内存空间了
        //进一步说明,构造函数知识单纯的给成员属性赋值,并不会分配内存空间
        //分配内存空间肯定是在定义的时候分配
        //PointA pa; 这么定义的时候  直接已经在栈中分配内存  调用无参构造函数初始化
        //Point *pa=new Point()  这么定义   已经在堆里分配内存  调用无参构造函数初始化
        //总结;构造函数只是初始化成员属性,并不分配内存
        this->x = p.x;
        this->y = p.y;
    }
    Point(int a = 1, int b = 1){
        this->x = a;
        this->y = b;
    }
    virtual void Print(){
        cout << "x=" << x << endl;
    }
private:
    int x;
    int y;
};

class PointA :public Point{
public:
    void Print(){
        cout << "我是子类,我重写的父类的成员函数" << endl;
    }
};

//赋值兼容原则
void ProtectA(Point pin){
    pin.Print();
}

void ProtectC(Point &pin){
    pin.Print();
}

void ProtectD(Point *pin){
    pin->Print();
}

void ProtectB(){
    PointA pa;
    ProtectA(pa);
    //这里发现  打印的是  x=乱码 ------   没有调用子类的Print()函数?
    //这个错误是因为ProtectA(Point pin),
    //调用ProtectA()函数,
    //①执行父类拷贝构造函数
    //②产生新的父类临时对象pin(此时pin已经是父类对象,他执行的是拷贝构造函数)
    //③父类对象调用virtual关键字修饰的函数,
    //c++编译会在父类内存区域向后搜索子类属性,发现没有子类属性
    //c++编译器判断这个对象时父类对象,直接调用父类的Print()

    ProtectC(pa);
    ProtectD(&pa);

}

void main(){
    ProtectB();
    system("pause");
}
时间: 2024-10-18 21:20:51

C++ 类的多态一(virtual关键字--构造函数深刻理解)的相关文章

条款7:为多态基类析构函数声明为virtual

基类指针指向子类对象. 子类对象必须位于堆.因此为了避免泄漏内存资源,当指针不使用时,delete掉每一个对象非常重要.但是如果基类的析构函数不声明为virtual.那么指向子类对象的指针delete时,析构掉的成分都是基类的,而子类的成分没有被识别出来,而未被析构掉.这样就造成资源浪费.给基类析构函数声明为virtual,那么delete对象时,就会是你希望的那样,它会销毁整个对象,包括所有继承类(drived class)的成分.析构时调用顺序:先调用最深层次的派生类的析构函数,依次向上调用

浅谈C#中new、override、virtual关键字的区别

OO思想现在已经在软件开发项目中广泛应用,其中最重要的一个特性就是继承,最近偶简单的学习了下在设计模式中涉及到继承这个特性时,所需要用到的关键字,其中有一些关键点,特地整理出来. 一.New 在C#中,new这个关键字使用频率非常高,主要有3个功能: a)   作为运算符用来创建一个对象和调用构造函数. b)   作为修饰符. c)   用于在泛型声明中约束可能用作类型参数的参数的类型. 在本文中,只具体介绍new作为修饰符的作用,在用作修饰符时,new关键字可以在派生类中隐藏基类的方法,也就说

c# 多态中 Virtual与override的作用

class A { public A() { PrintFields(); } public void PrintFields() { Console.WriteLine("123"); } } class B : A { int x = 1; int y; public B() { y = -1; } public new void PrintFields() { Console.WriteLine("x={0},y={1} ", x, y); } } publi

浅析C#中new、override、virtual关键字的区别

OO思想现在已经在软件开发项目中广泛应用,其中最重要的一个特性就是继承,最近偶简单的复习了下在C#中涉及到继承这个特性时,所需要用到的关键字,其中有一些关键点,特地整理出来,方便大家查阅. 一.在C#中,new这个关键字使用频率非常高,主要有3个功能: a)     作为运算符用来创建一个对象和调用构造函数. b)     作为修饰符. c)      用于在泛型声明中约束可能用作类型参数的参数的类型. 在本文中,只具体介绍new作为修饰符的作用,在用作修饰符时,new关键字可以在派生类中隐藏基

.NET Core CSharp初级篇 1-6 类的多态与继承

.NET Core CSharp初级篇 1-6 本节内容为类的多态与继承 简介 终于讲到了面向对象三大特性中的两大特性--继承与多态.通过继承与多态,我们能很好的将类的拓展性发挥到了极致.在下面的内容讲解中,我们将从各个方面对继承和多态进行刨析. 继承 继承事实上是一个非常好理解的语法,在C#中实现继承使用A:B,表示A类继承B类.子类(一称为派生类)继承于父类(一称为基类)就好比孩子继承了父亲的一切,但是孩子总归是有自己的个性的,而父亲也有一些不会告诉孩子的事情.如果我们从之前的访问控制符这一

C++ virtual 关键字总结

1.虚函数 如果不使用virtual关键字,当使用基类的指针p指向派生类的对象是,调用的p的一个方法(比如print)时,调用的是基类里面的print方法. 如果使用virtual关键字,则可以调用派生类里的print方法. class Base { public:Base(){} public: virtual void print(){cout<<"Base";} }; class Derived:public Base { public:Derived(){} pub

C++ 类的多态四(虚析构函数的重要性)

//虚析构函数的重要性 #include<iostream> using namespace std; /* 虚析构函数 主要用在多态中,用来释放子类对象内存空间,如果不使用虚析构函数, 那么在多态的场景下,使用delete关键字只能执行父类析构函数 子类对象中没有父类对象 父类中有虚函数,子类中重写了该虚函数,那么默认子类中重写的函数也是虚函数,子类中不写也可以 但是为了代码的可读性,还是在子类中也加上virtual 关键字 */ class Point{ public: Point(int

java学习中,面向对象的三大特性:封装、继承、多态 以及 super关键字和方法的重写(java 学习中的小记录)

java学习中,面向对象的三大特性:封装.继承.多态 以及 super关键字和方法的重写(java 学习中的小记录) 作者:王可利(Star·星星) 封装     权限修饰符:public 公共的,private 私有的     封装的步骤:          1.使用private 修饰需要封装的成员变量.          2.提供一个公开的方法设置或者访问私有的属性              设置 通过set方法,命名格式:     set属性名();  属性的首字母要大写 访问 通过ge

virtual 关键字

virtual?关键字用于修饰方法.属性.索引器或事件声明,并且允许在派生类中重写这些对象.例如,此方法可被任何继承它的类重写. public virtual double Area() { return x * y; } 备注: 调用虚方法时,将为重写成员检查该对象的运行时类型.将调用大部分派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员. 默认情况下,方法是非虚拟的.不能重写非虚方法. virtual?修饰符不能与?static.abstract, private?或?ov