问题总结

  • 问题一:关于动态内存分配
  1. new和malloc的区别是什么?
  2. delete和free的区别是什么?
  • new关键字与malloc函数的区别
  1. new关键字是C++的一部分
  2. malloc是由C库提供的函数
  3. new以具体类型为单位进行内存分配
  4. malloc以字节为单位进行内存分配
  5. new在申请内存空间时可以进行初始化
  6. malloc仅根据需要申请定量的内存空间
  7. new在所有C++编译器中都被支持
  8. malloc在某些系统开发中是不能被调用
  9. new能够触发构造函数的调用
  10. malloc仅分配需要的内存空间
  11. 对象的创建只能使用new
  12. malloc不适合面向对象开发
  • delete和free的区别
  1. delete在所有C++编译器中都被支持
  2. free在某些系统开发中是不能调用的
  3. delete能够触发析构函数的调用
  4. free仅归还之前分配的内存空间
  5. 对象的销毁只能使用delete
  6. free不适合面向对象开发
  • 范例程序
#include <iostream>
#include <string>
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()
{
    Derived* d = new Derived;
    Derived* m = (Derived*)malloc(sizeof(Derived));
    delete d;
    free(m);
}
  • 运行结果
  • 问题二:关于虚函数的问题
  1. 构造函数是否可以成为虚函数
  2. 析构函数是否可以成为虚函
  • 构造函数不可能成为虚函数,在构造函数执行结束后,虚函数表指针才会被正确的初始化
  • 析构函数可以成为虚函数,建议在设计类时将析构函数声明为虚函数
  • 范例程序(如果父类的析构函数没有声明虚函数会带来什么后果)
#include <iostream>
#include <string>
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;
    delete p;
}
  • 运行结果
观察运行结果,可以知道程序执行了父类的构造函数,子类的构造函数,在delete指针p时,程序只调用了父类的析构函数,没有调用子类的析构函数。如果在构造函数中我们动态申请了内存,在析构函数中进行是否可以内存的操作,恰恰程序却跳过了析构函数函数,这样就会出现内存泄漏的问题!!
解决方法:将析构函数声明为虚函数
  • 范例程序
#include <iostream>
#include <string>
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;
    delete p;
}
  • 运行结果
  • 构造函数中不可能发生多态行为,在构造函数执行时,虚函数表指针未被正确初始化
  • 析构函数中不可能发生多态行为,在析构函数执行时,虚函数表指针已经被销毁
  • 构造函数和析构函数中不能发生多态行为,只调用当前类中定义的函数版本!!
  • 范例程序
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
    Base()
    {
        print();
        cout << "Base()" << endl;
    }
    virtual ~Base()
    {
        cout << "~Base()" << endl;
    }
    virtual void print()
    {
        cout << "I'm Base" << endl;
    }
};
class Derived : public Base
{
public:
    Derived()
    {
        print();
        cout << "Derived()" << endl;
    }
    ~Derived()
    {
        cout << "~Derived()" << endl;
    }
    void print()
    {
        cout << "I'm Derived" << endl;
    }
};
int main()
{
    Base* bp = new Derived;
    delete bp;
}
  • 运行结果
观察运行结果发现,对象的类型子类,在构造函数中如果调用虚函数,执行的是自身定义的虚函数版本,这也证明了在构造函数和析构函数中不能发生多态行为!
  • 问题三:关于继承中的强制类型转换
  1. dynamic_cast是与继承相关的类型转换关键字
  2. dynamic_cast要求相关的类中必须有虚函数
  3. 用于有直接或者间接继承关系的指针(引用)之间,指针转换成功:得到目标类型的指针,指针转换失败:得到一个空指针。引用转换成功:得到目标类型的引用,转换失败:得到一个异常操作信息。
  4. 编译器会检查dynamic_cast的使用是否正确
  5. 类型转换的结果只可能在运行阶段才能得到
  • 范例程序
#include <iostream>
#include <string>
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 Derived;
    Derived* pd = dynamic_cast<Derived*>(p);
    if (pd != NULL)
    {
        cout << "pd = " << pd << endl;
    }
    else
    {
        cout << "Cast error!" << endl;
    }
    delete p;
    return 0;
}
  • 运行结果
  • 小结
  1. new/delete会触发析构函数和构造函数的调用
  2. 构造函数不能成为虚函数
  3. 析构函数可以成为虚函数
  4. 构造函数和析构函数中都无法产生多态行为
  5. dynamic_cast是与继承相关的专用转换关键字
posted @ 2020-02-04 21:05  认真做个普通人  阅读(115)  评论(0编辑  收藏  举报