【C++】多态总结

简介:

多态

多态是如何实现绑定的?

多态绑定分两种情况,一种是静态绑定即编译时多态,一种是动态绑定即运行时多态

  • 编译时多态:

是利用重载实现的。对于非虚函数的成员来说,系统在编译时,按照函数的参数的区别来绑定要实现的操作,在编译时就确定了调用哪个函数。

  • 运行时多态:

简单地说,虚函数是动态绑定的基础;动态绑定是实现运行时多态的基础。 要触发动态绑定,需满足两个条件: (1) 只有虚函数才能进行动态绑定,非虚函数不进行动态绑定。
(2) 必须通过基类类型的引用或指针进行函数调用。 通过基类指针或基类引用做形参,当实参传入不同的派生类(或基类)的指针或引用,在函数内部触发动态绑定,从而来运行时实现多态的。

所以说,为什么调用普通函数比调用虚函数的效率高?

因为普通函数是静态联编的,而调用虚函数是动态联编的。

  • 静态联编 :在编译的时候就确定了函数的地址,然后call就调用了。
  • 动态联编 : 首先需要取到对象的首地址,然后再解引用取到虚函数表的首地址后,再加上偏移量才能找到要调的虚函数,然后call调用。

明显动态联编要比静态联编做的操作多,肯定就费时间。

为什么要用虚函数表(存函数指针的数组)?

  • 实现多态,父类对象的指针指向父类对象调用的是父类的虚函数,指向子类调用的是子类的虚函数。
  • 同一个类的多个对象的虚函数表是同一个,所以这样就可以节省空间,一个类自己的虚函数和继承的虚函数还有重写父类的虚函数都会存在自己的虚函数表。

如果你想学习C/C++可以来这个裙,首先是330,中间是859,最后是766,里面可以学习和交流也有资料可以下载。

为什么要把基类的析构函数定义为虚函数?

在用基类操作派生类时,为了防止执行基类的析构函数,不执行派生类的析构函数。因为这样的删除只能够删除基类对象, 而不能删除子类对象, 形成了删除一半形象, 会造成内存泄漏.

为什么要把基类的析构函数定义为虚函数就可以调用子类析构函数呢?看下面代码

class Base
{
public:
    Base() {};
    virtual ~Base() {};
 
    virtual void DoSomething() 
    { 
        cout << "Do something in class Base!" << endl; 
        
    };
};
 
class Derived : public Base
{
public:
    Derived() {};
    ~Derived() 
    { 
        cout << "Output from the destructor of class ClxDerived!" << endl; 
        
    };
 
    void DoSomething() 
    { 
        cout << "Do something in class ClxDerived!" << endl; 
        
    };
};
int main()
{
    //操作1
    Base* p1 = new Derived;
    p1->DoSomething();
    delete p1;
    
    //操作2
    Base* p2 = new Base;
    p2->DoSomething();
    delete p2;
}

操作1:用派生类的指针去操作派生类的成员,释放指针P1的过程是:先释放派生类的资源,再释放基类资源。因为构造时先构造的基类,后构造派生类.

操作2:用基类的指针去操作派生类的成员,释放指针P的过程是:只是释放了基类的资源,而没有调用派生类的析构函数.因为派生类的DoSomething()函数重写了基类的,所以调用的是派生类的.

  • 这样的删除只能够删除基类对象, 而不能删除子类对象, 形成了删除一半形象, 造成内存泄漏.

在公有继承中, 基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员.如果想要用基类对非继承成员进行操作, 则要把基类的这个函数定义为虚函数.

所以为了解决操作2的问题,就要把基类析构函数定义成虚函数:

因为定义成虚函数后,子类的析构函数会对父类的析构函数重写,这样就构成多态,delete p2时,因为指向的子类对象,所以就去虚表里调用的就是子类的析构,子类的析构函数是合成的,所以先调用子类的析构函数,再调用符类的析构函数。这样就把子类和父类都析构了。

为什么子类和父类的函数名不一样,还可以构成重写呢?

因为编译器对析构函数的名字做了特殊处理,在内部函数名是一样的。

抽象类

抽象类是利用虚函数的延伸

virtual void Display() = 0;//存虚函数

含有存虚函数的类为抽象类,也叫接口类,不能实例出对象

抽象类应用

抽象类就是用来继承的,如多个子类继承同一个抽象类,就都可以重写这个共同父类的存虚函数,这样的话我们用父类的指针来接收子类,我们把哪个子类传给父类,就调用的是那个子类里的方法,是不是很方便,抽象类也是一种规范化的编程。

C++ static

  • 类的静态成员是属于这个类的,属于类的每一个对象
  • 基类的静态成员,不管这个基类派生出多少子类,只有这么一个静态成员。
  • 我们在使用时要注意:

(1)静态方法只能访问类的静态成员,不能访问类的非静态成员;
(2)非静态方法可以访问类的静态成员,也可以访问类的非静态成员;
(3)静态方法既可以用实例来调用,也可以用类名来调用。

c 中 static

  • 修饰全局变量,改变变量的连接属性,不能其他文件调用,不改变存储属性。
  • 修饰局部变量,改变存储属性,变为存储带静态全局区,不改变连接属性,因为它属于局部变量。
相关文章
|
2月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
42 2
C++入门12——详解多态1
|
7月前
|
C++
C++中的封装、继承与多态:深入理解与应用
C++中的封装、继承与多态:深入理解与应用
163 1
|
2月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
83 1
|
4月前
|
存储 编译器 C++
|
5月前
|
存储 编译器 C++
【C++】深度解剖多态(下)
【C++】深度解剖多态(下)
55 1
【C++】深度解剖多态(下)
|
5月前
|
存储 编译器 C++
|
5月前
|
机器学习/深度学习 算法 C++
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)
|
5月前
|
Java 编译器 C++
【C++】深度解剖多态(上)
【C++】深度解剖多态(上)
56 2
|
5月前
|
程序员 C++
【C++】揭开C++多态的神秘面纱
【C++】揭开C++多态的神秘面纱
|
5月前
|
机器学习/深度学习 PyTorch 算法框架/工具
C++多态崩溃问题之在PyTorch中,如何定义一个简单的线性回归模型
C++多态崩溃问题之在PyTorch中,如何定义一个简单的线性回归模型