第五十五课、经典问题解析四 一、new和malloc、delete和free之间的区别 二、构造函数、析构函数、虚函数、多态 三、继承中的强制类型转换 四、小结

1、new和malloc

(1)、new关键字是c++的一部分

            malloc是c库提供的函数

(2)、new是以具体类型为单位分配内存

            malloc是以字节为单位分配内存

(3)、new在申请内存空间时可进行初始化

    malloc仅根据需要申请定量的内存空间

(4)、new在所有c++编译器中都被支持

            malloc在某些系统开发中是不能调用的

(5)、new能触发构造函数的调用

     malloc仅分配需要的内存空间

(6)、对象的创建只能用new

       malloc不适合面向对象的开发

2、delete和free

(1)、delete在所有c++编译器中都被支持

            free在某些系统开发中是不能调用的

(2)、delete能触发析构函数的调用

           free仅归还之前分配的内存空间

(3)、对象的销毁只能用delete

     free不适合面向对象的开发

 

#include<iostream>
#include<cstdlib>//说明malloc和free是函数

using namespace std;

class Test
{
private:
    int *mp;
public:
    Test()
    {
        mp = new int(100);
        cout << "Test()" << endl;
        cout << *mp << endl;
    }
    
    ~Test()
    {
        delete mp;
cout
<< "~Test()" << endl; } }; int main() { Test* t1 = new Test();//会调用构造函数,不能用free来释放,否则可能造成内存泄漏 Test* t2 = (Test *)malloc(sizeof(Test));//只分配需要的内存空间,不会调用构造函数,不能用delete释放,否则会将mp指针错误释放掉

delete t1;//调用析构函数 free(t2);//不会调用析构函数
return 0; }

二、构造函数、析构函数、虚函数、多态

1、构造函数不可以成为虚函数

(1)、在构造函数执行结束后,虚函数表指针才会被正确初始化

2、析构函数可以成为虚函数

(1)、建议在设计类时将析构函数声明为虚函数

3、构造函数里面不可能发生多态行为

(1)、构造函数执行时,虚函数表指针未被正确初始化

4、析构函数里面不可能发生多态行为

(1)、在析构函数执行时,虚函数表指针已被销毁

 

#include<iostream>


using namespace std;

class Base
{
public:
    Base()// error, can not be declared virtual
    {
        cout << "Base()" << endl;
        func();//不会发生多态,调用当前类里面的func()
    }
    
    virtual void func()
    {
        cout << "Base::func()" << endl;
    }
    
    virtual ~Base()//can be declared virtual
    {
        cout << "~Base()" << endl;
        func();//只调用当前类定义的版本
    }
};

class Derived : public Base
{
public:
    Derived()
    {
        cout << "Derived()" << endl;
        func();
    }
    virtual void func()
    {
        cout << "Derived::func()" << endl;
    }
    virtual ~Derived()
    {
        cout << "~Derived()" << endl;
        func();
    }
};

int main()
{
    Base* p = new Derived();//赋值兼容性原则
    
    cout << endl;
    
    delete p;//若析构函数不是虚函数,则这里就会只调用父类的构造函数,而不调用子类的,会造成内存泄漏
       //根据析构顺序,会先调用父类析构函数,然后调用子类析构函数
return 0; } //打印结果 /* Base() Base::func() Derived() Derived::func() ~Derived() Derived::func() ~Base() Base::func() */

三、继承中的强制类型转换

1、dynamic_cast与继承相关的类型转换关键字

2、dynamic_cast要求相关类中必须有虚函数

3、用于有直接或间接继承关系的指针(引用)之间

(1)、指针

A、转换成功:得到目标类型的指针

B、转换失败:得到一个空指针

(2)、引用

A、转换成功:得到目标类型的引用

B、转换失败:得到一个异常操作信息

 

#include<iostream>


using namespace std;

class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    
     virtual ~Base()//使用dynamic_cast需要类中有虚函数,故这里将析构函数声明为虚函数
    {
        cout << "~Base()" << endl;
    }
};

class Derived : public Base
{
};

int main()
{
    Base* p = new Base();
    
    Derived* d = dynamic_cast<Derived*>(p);//dynamic_cast 可以将子类转换转换成父类,反之不行(子类中可能有新成员),可以判断父子关系
    
   // Derived* p = new Derived();
    //Base* d = dynamic_cast<Base*> (p);//这样就会转换成功,输出succefully
    
    if(d != NULL)
    {
        cout << "succefully!" << endl;
    }
    else
    {
        cout << "error" << endl;//结果会输出这个,说明转换失败
    }
    
    cout << endl;
    
    delete p;
    return 0;
}

四、小结

(1)、new/delete会触发构造函数或者析构函数的调用

(2)、构造函数不能成为虚函数

(3)、析构函数可以成为虚函数

(4)、构造函数和析构函数里面不能产生多态的行为

(5)、dynamic_cast 关键字是与继承相关