zoukankan      html  css  js  c++  java
  • C++解析(25):关于动态内存分配、虚函数和继承中强制类型转换的疑问

    0.目录

    1.动态内存分配

    2.虚函数

    3.继承中的强制类型转换

    4.小结

    1.动态内存分配

    1.1 new和malloc的区别

    new关键字与malloc函数的区别:

    • new关键字是C++的一部分
    • malloc是由C库提供的函数
    • new以具体类型为单位进行内存分配
    • malloc以字节为单位进行内存分配
    • new在申请内存空间时可进行初始化
    • malloc仅根据需要申请定量的内存空间

    下面的代码输出什么?为什么?

    示例——new和malloc的区别:

    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    
    class Test
    {
    public:
        Test()
        {
            cout << "Test::Test()" << endl;
        }
    };
    
    int main()
    {
        Test* pn = new Test;
        Test* pm = (Test*)malloc(sizeof(Test));
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    Test::Test()
    

    newmalloc的区别:

    • new在所有C++编译器中都被支持
    • malloc在某些系统开发中是不能调用
    • new能够触发构造函数的调用
    • malloc仅分配需要的内存空间
    • 对象的创建只能使用new
    • malloc不适合面向对象开发

    1.2 delete和free的区别

    下面的代码输出什么?为什么?

    示例——delete和free的区别:

    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    
    class Test
    {
        int* mp;
    public:
        Test()
        {
            cout << "Test::Test()" << endl;
            
            mp = new int(100);
            
            cout << *mp << endl;
        }
        ~Test()
        {
            delete mp;
            
            cout << "~Test::Test()" << endl;
        }
    };
    
    int main()
    {
        Test* pn = new Test;
        Test* pm = (Test*)malloc(sizeof(Test));
        
        free(pn);
        free(pm);
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    Test::Test()
    100
    

    可以看到,free不会触发析构函数,会造成内存泄漏!

    deletefree的区别:

    • delete在所有C++编译器中都被支持
    • free在某些系统开发中是不能调用
    • delete能够触发析构函数的调用
    • free仅归还之前分配的内存空间
    • 对象的销毁只能使用delete
    • free不适合面向对象开发

    2.虚函数

    2.1 构造函数与析构函数是否可以成为虚函数?

    构造函数是否可以成为虚函数析构函数是否可以成为虚函数
    构造函数不可能成为虚函数:

    • 在构造函数执行结束后,虚函数表指针才会被正确的初始化

    析构函数可以成为虚函数:

    • 建议在设计类时将析构函数声明为虚函数

    示例——不把析构声明为虚函数:

    #include <iostream>
    
    using namespace std;
    
    class Base
    {
    public:
        Base()
        {
            cout << "Base()" << endl;
        }
        
        ~Base()
        {
            cout << "~Base()" << endl;
        }
    };
    
    
    class Derived : public Base
    {
    public:
        Derived()
        {
            cout << "Derived()" << endl;
        }
        
        ~Derived()
        {
            cout << "~Derived()" << endl;
        }
    };
    
    int main()
    {
        Base* p = new Derived();
        
        cout << endl;
        
        delete p;
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    Base()
    Derived()
    
    ~Base()
    

    由于没有把析构声明为虚函数,因此,编译器直接根据指针p的类型来决定调用哪一个析构函数,又由于指针p的类型是父类的,所以编译器认为直接调用父类的析构函数就可以了。

    示例——把析构声明为虚函数:

    #include <iostream>
    
    using namespace std;
    
    class Base
    {
    public:
        Base()
        {
            cout << "Base()" << endl;
        }
        
        virtual ~Base()
        {
            cout << "~Base()" << endl;
        }
    };
    
    
    class Derived : public Base
    {
    public:
        Derived()
        {
            cout << "Derived()" << endl;
        }
        
        ~Derived()
        {
            cout << "~Derived()" << endl;
        }
    };
    
    int main()
    {
        Base* p = new Derived();
        
        cout << endl;
        
        delete p;
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    Base()
    Derived()
    
    ~Derived()
    ~Base()
    

    如果将构造函数声明为虚函数,报错信息如下:

    error: constructors cannot be declared virtual
    

    2.2 构造函数与析构函数是否可以发生多态?

    构造函数中是否可以发生多态?析构函数中是否可以发生多态?
    构造函数中不可能发生多态行为:

    • 在构造函数执行时,虚函数表指针未被正确初始化

    析构函数中不可能发生多态行为:

    • 在析构函数执行时,虚函数表指针已经被销毁

    构造函数和析构函数中不能发生多态行为只调用当前类中定义的函数版本!!

    示例——构造函数和析构函数中不可能发生多态:

    #include <iostream>
    
    using namespace std;
    
    class Base
    {
    public:
        Base()
        {
            cout << "Base()" << endl;
            func();
        }
        
        virtual void func() 
        {
            cout << "Base::func()" << endl;
        }
        
        virtual ~Base()
        {
            func();
            cout << "~Base()" << endl;
        }
    };
    
    
    class Derived : public Base
    {
    public:
        Derived()
        {
            cout << "Derived()" << endl;
            func();
        }
        
        virtual void func()
        {
            cout << "Derived::func()" << endl;
        }
        
        ~Derived()
        {
            func();
            cout << "~Derived()" << endl;
        }
    };
    
    int main()
    {
        Base* p = new Derived();
        
        cout << endl;
        
        delete p;
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    Base()
    Base::func()
    Derived()
    Derived::func()
    
    Derived::func()
    ~Derived()
    Base::func()
    ~Base()
    

    3.继承中的强制类型转换

    继承中如何正确的使用强制类型转换

    dynamic_cast是与继承相关的类型转换关键字
    dynamic_cast要求相关的类中必须有虚函数
    用于有直接或者间接继承关系指针(引用)之间

    • 指针:
      1. 转换成功:得到目标类型的指针
      2. 转换失败:得到一个空指针
    • 引用:
      1. 转换成功:得到目标类型的引用
      2. 转换失败:得到一个异常操作信息

    编译器会检查dynamic_cast的使用是否正确
    类型转换的结果只可能在运行阶段才能得到

    示例——dynamic_cast的使用:

    #include <iostream>
    
    using namespace std;
    
    class Base
    {
    public:
        Base()
        {
            cout << "Base::Base()" << endl;
        }
        
        virtual ~Base()
        {
            cout << "Base::~Base()" << endl;
        }
    };
    
    class Derived : public Base
    {
    };
    
    int main()
    {
        Base* p = new Base;
        
        Derived* pd = dynamic_cast<Derived*>(p);
        
        if( pd != NULL )
        {
            cout << "pd = " << pd << endl;
        }
        else
        {
            cout << "Cast error!" << endl;
        }
        
        delete p;
        
        return 0;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    Base::Base()
    Cast error!
    Base::~Base()
    

    4.小结

    • new / delete会触发构造函数或者析构函数的调用
    • 构造函数不能成为虚函数
    • 析构函数可以成为虚函数
    • 构造函数析构函数中都无法产生多态行为
    • dynamic_cast是与继承相关的专用转换关键字
  • 相关阅读:
    极光推送
    浅谈Android的屏幕适配问题
    Git/GitHub学习第一天
    Android学习总结
    为什么现在开始写你的第一篇博客
    escape.alf.nu XSS Challenges 0-7 之一步步学XSS
    PE文件学习
    通过sqli-labs学习sql注入——基础挑战之less11-22
    通过sqli-labs学习sql注入——基础挑战之less1-10
    重学数据结构系列之——总结
  • 原文地址:https://www.cnblogs.com/PyLearn/p/10092188.html
Copyright © 2011-2022 走看看