问题总结
-
问题一:关于动态内存分配
-
new和malloc的区别是什么?
-
delete和free的区别是什么?
-
new关键字与malloc函数的区别
-
new关键字是C++的一部分
-
malloc是由C库提供的函数
-
new以具体类型为单位进行内存分配
-
malloc以字节为单位进行内存分配
-
new在申请内存空间时可以进行初始化
-
malloc仅根据需要申请定量的内存空间
-
new在所有C++编译器中都被支持
-
malloc在某些系统开发中是不能被调用
-
new能够触发构造函数的调用
-
malloc仅分配需要的内存空间
-
对象的创建只能使用new
-
malloc不适合面向对象开发
-
delete和free的区别
-
delete在所有C++编译器中都被支持
-
free在某些系统开发中是不能调用的
-
delete能够触发析构函数的调用
-
free仅归还之前分配的内存空间
-
对象的销毁只能使用delete
-
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); }
-
运行结果
-
问题二:关于虚函数的问题
-
构造函数是否可以成为虚函数
-
析构函数是否可以成为虚函
-
构造函数不可能成为虚函数,在构造函数执行结束后,虚函数表指针才会被正确的初始化
-
析构函数可以成为虚函数,建议在设计类时将析构函数声明为虚函数
-
范例程序(如果父类的析构函数没有声明虚函数会带来什么后果)
#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; }
-
运行结果
观察运行结果发现,对象的类型子类,在构造函数中如果调用虚函数,执行的是自身定义的虚函数版本,这也证明了在构造函数和析构函数中不能发生多态行为!
-
问题三:关于继承中的强制类型转换
-
dynamic_cast是与继承相关的类型转换关键字
-
dynamic_cast要求相关的类中必须有虚函数
-
用于有直接或者间接继承关系的指针(引用)之间,指针转换成功:得到目标类型的指针,指针转换失败:得到一个空指针。引用转换成功:得到目标类型的引用,转换失败:得到一个异常操作信息。
-
编译器会检查dynamic_cast的使用是否正确
-
类型转换的结果只可能在运行阶段才能得到
-
范例程序
#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; }
-
运行结果
-
小结
-
new/delete会触发析构函数和构造函数的调用
-
构造函数不能成为虚函数
-
析构函数可以成为虚函数
-
构造函数和析构函数中都无法产生多态行为
-
dynamic_cast是与继承相关的专用转换关键字
主要记录的是学习听课的笔记