1.动态内存分配
new-delete malloc-free
(1)new关键字和malloc函数的区别
new关键字是C++的一部分,malloc是由C库提供的函数
new以具体类型为单位进行内存分配,malloc以字节为单位进行内存分配
new在申请内存空间时可进行初始化,malloc仅根据需要申请定量的内存空间
#include <iostream>
#include<string>
using namespace std;
class Test {
public:
Test() {
cout << "Test::Test()" << endl;
}
~Test() {
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分配时不仅分配了内存,还创建了对象,即调用了构造函数,所以delete时,不仅要释放内存还要摧毁对象;而malloc仅仅是分配内存,free仅仅是将内存归还给系统,所以new、malloc、delete、free不可混用
对象的创建只能使用new,malloc不适合面向对象开发
2.关于虚函数
构造函数和析构函数是否可以成为虚函数?
虚函数依赖虚函数表(vtable) 和虚表指针(vptr) 实现
(1)构造函数不可能成为虚函数
在构造函数执行结束后,虚函数表指针才会被正确的初始化,如果构造函数是虚函数,调用时需要通过 vptr 找虚表,但此时 vptr 还未初始化,根本无法定位虚函数;
(2)析构函数建议成为虚函数
建议在设计类时将析构函数声明为虚函数,如果基类指针指向派生类对象,当通过基类指针释放对象时,若基类析构函数不是虚函数,编译器只会调用基类的析构函数,派生类的析构函数不会执行,导致子类的资源(堆内存)无法释放,造成内存泄漏;
例如:
如果析构函数不为虚函数,则子类对象不会被释放,会有内存泄漏
#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* ptr = new Derived(); // 基类指针指向派生类对象
delete ptr; // 仅调用Base析构,Derived析构未执行
return 0;
}
运行结果:
Base构造 Derived构造 Base析构
如果是虚函数:
#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* ptr = new Derived();
delete ptr; // 先调用Derived析构,再调用Base析构
return 0;
}
运行结果:
Base构造 Derived构造 Derived析构 Base析构
构造函数和析构函数是否可以发生多态?
(1)构造函数中不可能发生多态
在构造函数执行时,虚函数表指针未被正确初始化
(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() {
func();
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()
Base::func()
Derived()
~Derived()
Base::func()
~Base()
#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()
3.继承中如何正确使用强制类型转化?
(1)dynamic_cast是与继承相关的类型转换关键字
(2)dynamic_cast要求相关的类中必须有虚函数
(3)用于有直接或间接继承关系的指针或引用之间
指针:若转换成功,则得到目标类型的指针,若转换失败,则得到一个空指针
引用:若转换成功,则得到目标类型的引用,若转换失败,则得到一个异常操作信息
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 = p; 报错,所以要使用强制类型转换dynamic_cast,但是相关的类中要有虚函数
Derived* pd = dynamic_cast<Derived*>(p);
cout << "pd=" << pd << endl;
return 0;
}
运行结果:
Base::Base()
pd=013F5F48
若指针转换失败,则得到一个空指针
#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 << "error!" << endl;
}
return 0;
}
运行结果:
Base::Base()
error!