---- 整理自狄泰软件唐佐林老师课程
1. 关于动态内存分配
1.1 问题一:new 和 malloc 的区别
1.1.1 new 关键字和 malloc 函数的区别
new 关键字是 C++ 的一部分 | malloc 是由 C 库函数提供的函数 |
---|
new 以具体 类型 为单位进行内存分配 | malloc 以 字节 为单位进行内存分配 |
new 在申请内存空间时可进行初始化 | malloc 仅根据需要申请定量的内存空间 |
new 在所有 C++ 编译器都被支持 | malloc 在某些系统开发中是不能调用的 |
new 能够触发构造函数的调用 | malloc 仅分配需要的内存空间 |
对象的创建只能使用 new | malloc 不适合面向对象开发 |
1.1.2 编程实验:new 和 malloc 的区别一
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/3d01c4e8ca124837754bc9f8896dd4e0.png)
#include <iostream>
#include <string>
using namespace std;
class Test
{
public:
Test() {
cout << "Test()" << endl;
}
};
int main()
{
Test* pn = new Test;
Test* pm = (Test*)malloc(sizeof(Test));
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/cb6808f11ac4cda15f1b4360bfdc5e56.png)
1.1.3 编程实验:new 和 malloc 的区别二
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/4d11561f21573c222ac260333c165912.png)
#include <iostream>
#include <string>
using namespace std;
class Test
{
public:
Test() {
cout << "Test()" << endl;
}
~Test() {
cout << "~Test()" << endl;
}
};
int main()
{
Test* pn = new Test;
Test* pm = (Test*)malloc(sizeof(Test));
delete pn;
free(pm);
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2bdd3124fe3647d5087b0768d6a1e9ee.png)
1.2 问题二:delete 和 free 的区别
delete 在所有 C++ 编译器中都被支持 | free 在某些系统开发中是不能调用的 |
---|
delete 能够触发析构函数的调用 | free 仅归还之前分配的内存空间 |
对象的销毁只能使用 delete | free 不适合面向对象开发 |
2. 关于虚函数
2.1 问题一
- 编程实验:构造、析构、虚函数
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
virtual Base() {}
~Base() {}
};
class Derived : public Base
{
public:
Derived() {}
~Derived() {}
};
int main()
{
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/9b0ac113ae5075c92009804b73fcebc9.png)
#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;
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/48615cc02d3efdf1b248fedcc427d7a1.png)
#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;
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e37dbce4e08074046a38e1dcd5c06d64.png)
2.2 问题二
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
Base() {
cout << "Base()" << endl;
func();
}
virtual void func() {
cout << "Base::func()" << endl;
}
virtual ~Base() {
cout << "~Base()" << endl;
func();
}
};
class Derived : public Base
{
public:
Derived() {
cout << "Derived()" << endl;
func();
}
virtual void func() {
cout << "Derived::func()" << endl;
}
~Derived() {
cout << "~Derived()" << endl;
func();
}
};
int main()
{
Base* p = new Derived();
delete p;
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/9507a417825cecdc62265a8eeee523e1.png)
3. 关于继承中的强制类型转换 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 Base;
Derived* pd = dynamic_cast<Derived*>(p);
if (pd != NULL) {
cout << "pd = " << pd << endl;
} else {
cout << "Cast error!" << endl;
}
delete p;
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f4109699b0c7f741833aba7f5ec48a8f.png)
#include <iostream>
using namespace std;
class A {
public:
virtual ~A() { cout<<"~A()"<<endl; }
};
class B : public A{
public:
virtual ~B() { cout<<"~B()"<<endl; }
};
class C : public B{
public:
virtual ~C() { cout<<"~C()"<<endl; }
};
class D :public C {
public:
virtual ~D() { cout<<"~D()"<<endl; }
};
int main(){
A* pA;
B* pB;
C* pC;
D* pD = new D;
pA = dynamic_cast<A*>(pD);
if (pA == NULL){
cout<<"Upcasting failed: D* to A*"<<endl;
} else {
cout<<"Upcasting successfully: D* to A*"<<endl;
}
pB = dynamic_cast<B*>(pD);
if (pB == NULL){
cout<<"Upcasting failed: D* to B*"<<endl;
} else {
cout<<"Upcasting successfully: D* to B*"<<endl;
}
pC = dynamic_cast<C*>(pD);
if (pC == NULL){
cout<<"Upcasting failed: D* to C*"<<endl;
} else {
cout<<"Upcasting successfully: D* to C*"<<endl;
}
cout << endl;
delete pD;
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/633dd8b925931ed84102812a8a666286.png)
#include <iostream>
using namespace std;
class A {
public:
virtual ~A() { cout<<"~A()"<<endl; }
};
class B : public A{
public:
virtual ~B() { cout<<"~B()"<<endl; }
};
class C : public B{
public:
virtual ~C() { cout<<"~C()"<<endl; }
};
class D :public C {
public:
virtual ~D() { cout<<"~D()"<<endl; }
};
int main(){
A* pA = new A;
B* pB;
C* pC;
D* pD;
pB = dynamic_cast<B*>(pA);
if (pB == NULL){
cout<<"Downcasting failed: A* to B*"<<endl;
} else {
cout<<"Downcasting successfully: A* to B*"<<endl;
}
pC = dynamic_cast<C*>(pA);
if (pC == NULL){
cout<<"Downcasting failed: A* to C*"<<endl;
} else {
cout<<"Downcasting successfully: A* to C*"<<endl;
}
pD = dynamic_cast<D*>(pA);
if (pD == NULL){
cout<<"Downcasting failed: A* to D*"<<endl;
} else {
cout<<"Downcasting successfully: A* to D*"<<endl;
}
cout << endl;
delete pA;
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/80aa8faadccb7f665bef3dc25ea35e56.png)
参考引用自:http://c.biancheng.net/view/2343.html
- 上述代码中,类的继承顺序是
class D:class C:class B:class A
,
- 当pA是指向A类型的对象时(
pA = new A;
),向下转型失败,pA不能转换为B*、C*、D*类型。 - 当pD是指向D类型的对象时(
pD = new D;
),向上转型成功,pD可以转换为A*、B*、D*类型。 - 原因:因为每个类都会在内存中保存一份类型信息,编译器会将存在继承关系的类的类型信息使用指针“连接”起来,从而形成一个继承链,如下所示。
当使用 dynamic_cast 对指针进行类型转换时,程序会先找到该指针指向的对象,再根据对象找到当前类(指针指向的对象所属的类)的类型信息,并从此节点开始沿着继承链向上遍历,如果找到了要转化的目标类型,那么说明这种转换是安全的,就能够转换成功,如果没有找到要转换的目标类型,那么说明这种转换存在较大的风险,就不能转换。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e95eee7d992f806b2d1bef6f6c2d9b0f.png)
- 补充:
- dynamic_cast 用于在类的继承层次之间进行类型转换,它既允许向上转型(Upcasting),也允许向下转型(Downcasting)。向上转型是无条件的,不会进行任何检测,所以都能成功;向下转型的前提必须是安全的,要借助 RTTI 进行检测,所有只有一部分能成功。
- dynamic_cast 与 static_cast 是相对的,dynamic_cast 是“动态转换”的意思,static_cast 是“静态转换”的意思。dynamic_cast 会在程序运行期间借助 RTTI 进行类型转换,这就要求基类必须包含虚函数;static_cast 在编译期间完成类型转换,能够更加及时地发现错误。
- 向上转型时,只要待转换的两个类型之间存在继承关系,并且基类包含了虚函数(这些信息在编译期间就能确定),就一定能转换成功。因为向上转型始终是安全的,所以 dynamic_cast 不会进行任何运行期间的检查,这个时候的 dynamic_cast 和 static_cast 就没有什么区别了。
- 向下转型是有风险的,dynamic_cast 会借助 RTTI 信息进行检测,确定安全的才能转换成功,否则就转换失败。
- RTTI代表运行时类型信息,它提供了运行时确定对象类型的方法。
4. 小结
- new / delete会触发构造函数或者析构函数的调用
- 构造函数不能成为虚函数
- 析构函数可以成为虚函数
- 构造函数和析构函数中都无法产生多态行为
- dynamic_cast是与继承相关的专用关键字