多态
定义
个人理解:同一套函数,在不同类型的子类对象传入参数时,在不改变函数代码的前提下实现不同的逻辑。
示例
class A {
public:
virtual void do_something() const {
cout << "do_something in A" << endl;
}
};
class B : public A {
public:
void do_something() const {
cout << "do_something in B" << endl;
}
};
void call(const A& o) { // 其他子类也可以定义不同的do_something函数来实现自己的逻辑
o.do_something();
}
int main() {
B b;
call(b);
return 0;
}
虚析构函数
首先需要说明的是,存在多态的前提下才会诞生虚析构函数。首先说不涉及多态的情况,示例代码如下所示:
class A {
public:
A() {
cout << "in A" << endl;
}
~A() {
cout << "in ~A" << endl;
}
};
class B : public A {
public:
B() {
cout << "in B" << endl;
}
~B() {
cout << "in ~B" << endl;
}
};
int main() {
B* b = new B();
delete b;
return 0;
}
上述代码运行结果如下,这说明子类对象构造时先构造父类对象之后构造子类对象,子类对象析构时先析构子类对象之后析构父类对象,构造和析构是对称的。且这个时候父类的析构函数不管是不是被定义成虚析构函数,子类对象都能够成功析构。出现这种现象的原因是子类能够找到父类的析构函数的函数指针,所以在析构完子类时能够成功调用父类的析构函数。
in A
in B
in ~B
in ~A
而多态情况下父类析构函数不定义成虚函数,则会出现内存泄露问题,因为在父类看来找不到子类的析构函数指针在哪,而解决这个问题的关键在于把父类的析构函数定义成虚函数,这样在父类的虚函数表中就会记录子类的析构函数的函数指针的位置,示例代码如下所示:
class A {
public:
A() {
cout << "in A" << endl;
}
virtual ~A() { // 虚函数标识符很关键
cout << "in ~A" << endl;
}
};
class B : public A {
public:
B() {
cout << "in B" << endl;
}
~B() {
cout << "in ~B" << endl;
}
};
int main() {
A* a = new B();
delete a;
return 0;
}
extern & static
extern
变量和函数作用域
如果用定义全局函数或者变量,则函数和变量默认是extern的,即全局可见的,即使其他文件没有include这些函数和变量,也是可以通过extern声明来找到这些函数和变量,示例代码如下所示:
// a.cpp
#include <iostream>
using namespace std;
void print() {
cout << "print in a.cpp" << endl;
}
// main.cpp
#include <iostream>
extern void print();
int main() {
print(); // 即使main.cpp没有include a.cpp,依然能够正常调用a.cpp中定义的print函数
return 0;
}
告知编译器需要用C语言方式解析代码
extern c {} 这种方式通知编译器用解析C语言的方式解析代码。
static
隐藏
对于非成员函数或者变量,如果一个函数或者变量定义为static,则这个变量或者函数只能在可见状态下被调用,下述例子会解释该作用。
// a.cpp
#include <iostream>
using namespace std;
static void print() {
cout << "print in a.cpp" << endl;
}
// main.cpp
#include <iostream>
extern void print();
int main() {
print(); // 编译报错,只有当显示include a.cpp,代码才会编译通过
return 0;
}
对于static成员函数,其只能访问static成员变量,因为这些函数没有this指针,不能指向本个对象中的成员;对于static成员变量,只能通过在类外定义的方式进行变量定义,示例代码如下所示。
class A{
static int a;
static int a = 0; //非法
const static int a = 0; // 合法
}
int A::a = 0; // 对于非const static型变量进行类外定义。
int main {
int b = A::a; // 合法
return 0;
}
内容持久
存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。
默认初始化为0
其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。
new & malloc 区别
申请内存所在位置
new申请内存的位置为自由存储区,自由存储区可以是堆,也可以是静态存储区。malloc申请内存位置只能为堆。
返回类型安全性
new返回的是某种类型的指针,比较安全。malloc返回的是void*,该指针可以转换成任意指针,不太安全。
内存分配失败时的返回值
new失败会抛异常,malloc失败会返回nullptr。
是否指定内存大小
new不用指定,malloc需要指定。
调用构造函数
new执行的过程中会调用某种类型的构造函数,malloc则不会调用某种类型的构造函数,因而new更适用于创建一个自定义的类型,malloc更适用于创建一个内置类型。