关于动态内存分配
new 和 malloc 的区别是什么?
delete 和 free 的区别是什么?
new 关键字和 malloc 函数的区别
- new 关键字是 C++ 的一部分
- malloc 是 C 库提供的函数
- new 以具体类型为单位进行内存分配
- malloc 以字节为单位进行内存分配
- new 在申请内存空间时可进行初始化
- malloc 仅根据需要申请定量的内存空间
编程实验: new、delete 与 malloc、free
#include <iostream>
#include <cstdlib>
using namespace std;
class Test
{
private:
int* mp;
public:
Test()
{
cout << "Test::Test()" << endl;
mp = new int(100);
}
~Test()
{
delete mp;
cout << "~Test::Test()" << endl;
}
};
int main()
{
Test* pn = new Test;
Test* pm = (Test*)malloc(sizeof(Test));
delete pn;
free (pm);
return 0;
}
输出:
Test::Test()
~Test::Test()
分析:
new Test; ==> 在堆上创建一个对象,构造函数被调用
(Test*)malloc(sizeof(Test)); ==> 在堆上申请 sizeof(Test) 大小内存,构造函数未被调用,对象未正常创建
delete pn; ==> 销毁对象, 归还内存,析构函数被调用
free(pm); ==> 仅归还内存,析构函数未被调用
当 delete 与 free 混用,会发生什么呢?
delete --> free :
int main()
{
Test* pn = new Test;
free (pn); // 注意这里!
return 0;
}
输出:
Test::Test()
分析:
free 可以释放 new 申请的堆空间,但析构函数未被调用,对象未正常销毁(实例中,导致系统资源泄漏!!)
free --> delete :
int main()
{
Test* pm = (Test*)malloc(sizeof(Test));
delete pm; // 注意这里!
return 0;
}
输出:
~Test::Test()
分析:
delete 可以释放 malloc 申请的堆空间,不合法对象的析构函数被调用!运行结果将是不确定的!(示例中,将delete一个野指针指向的内存空间)
结论: C++ 中杜绝 malloc、 free 的使用
new 和 malloc 的区别
- new 在所有 C++ 编译器中都被支持
- malloc 在某些系统开发中不能调用
- new 能够触发构造函数的调用
- malloc 仅分配需要的内存空间
- 对象的创建只能使用 new
- malloc 不适合面向对象开发
delete 和 free 的区别
- delete 在所有 C++ 编译器中都被支持
- free 在某些系统开发中不能调用
- delete 能够触发析构函数的调用
- free 仅归还之前分配的内存空间
- 对象的销毁只能使用 delete
- free 不适合面向对象开发
关于虚函数
构造函数是否可以成为虚函数?
析构函数是否可以成为虚函数?
构造函数不可能成为虚函数
- 在构造函数执行结束后,虚函数表指针才会被正确的初始化
析构函数可以成为虚函数
- 析构函数在对象销毁之前被调用,意味着虚函数表指针仍然正确的指向虚函数表
- 建议在设计类时将析构函数声明为虚函数
编程实验: 构造,析构,虚函数
test_1.cpp
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
virtual void func()
{
cout << "Base::func()" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
};
class Derived : public Base
{
public:
Derived()
{
cout << "Derived()" << endl;
}
virtual void func()
{
cout << "Derived::func()" << endl;
}
~Derived()
{
cout << "~Derived()" << endl;
}
};
int main()
{
Base* p = new Derived(); // 注意这里!
// ...
delete p;
return 0;
}
输出:
Base()
Derived()
~Base()
分析:
为什么 子类 的析构函数没有被调用呢?
Base* p = new Derived(); ==> 因为赋值兼容性,编译通过。
delete p; ==> 编译器考虑安全性,根据指针类型进行对象销毁
析构函数声明为虚函数的意义 1:test_2.cpp
#include <iostream>
#include <string>
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();
// ...
delete p;
return 0;
}
输出:
Base()
Base::func()
Derived()
Derived::func()
Derived::func()
~Derived()
Base::func()
~Base()
分析:
当析构函数为虚函数时, delete p; 将根据 p 指向的实际对象决定如何调用析构函数。
析构函数发生多态行为,保证系统资源尽可能得到释放!
当声明构造函数为虚函数时,g++ 报错: virtual Base() { }
error: constructors cannot be declared virtual
构造函数中是否可以发生多态?
析构函数中是否可以发生多态?
构造函数中不可能发生多态行为
- 在构造函数执行时,虚函数表指针未正确初始化
析构函数中不可能发生多态行为
- 在析构函数执行时,虚函数表指针已经被销毁
构造函数和析构函数中不能发生多态行为, 只调用当前类中定义的函数版本!
关于继承中的强制类型转换
继承中如何正确的使用强制类型转换?
- dynamic_cast 是与继承相关的类型转换关键字
- dynamic_cast 要求相关的类中必须有虚函数
用于直接或间接继承关系的指针(引用)之间
指针:
- 转换成功: 得到目标类型指针
- 转换失败: 得到一个空指针
引用:
- 转换成功: 得到目标类型引用
- 转换失败: 得到一个异常操作信息
- 编译器会检查 dynamic_cast 的使用是否正确
- 类型转换的结果只可能在运行阶段才能得到
编程实验: dynamic_cast 的使用
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
virtual ~Base()
{
cout << "~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;
}
输出:
Base()
Cast error!
~Base()
析构函数声明为虚函数的意义 2 :
析构函数被声明为虚函数,保证 dynamic_cast 关键字可以被支持,而无需单独刻意定义其它虚成员函数
小结
- new / delete 会触发构造函数或者析构函数
- 构造函数不能成为虚函数
- 析构函数可以成为虚函数(推荐析构函数成为虚函数)
- 构造函数和析构函数中都无法产生多态行为
- dynamic_cast 是与继承相关的专用转换关键字
以上内容参考狄泰软件学院系列课程,请大家保护原创!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。