explorer

万丈高楼平地起,勿在浮沙筑高台

0%

传统指针具有以下缺陷:

  1. 单从一个指针的声明,无法判定它是指向一个对象还是指向一个包含该对象的数组
  2. 单从一个指针的声明,无法判定当不使用该指针时,是否需要释放它所指向对象所占用的资源
  3. 当需要释放指针所指向对象的资源时,并不能明确的知道是该使用delete,还是使用其它专有的释放函数
  4. 当需要使用delete释放资源时,到底是使用delete还是delete[],这需要小心使用,否则会造成内存泄漏或未定义行为
  5. 当确定了释放机制时,也有可能写代码时一不小心,就造成了 double free
  6. 当释放一个资源时,有可能还有其它指针(Dangling pointers)指向该资源,从而导致很多很难查的 BUG

使用智能指针变能够最大化的避免以上问题。

阅读全文 »

为保证多态析构的正确性,需要为基类的虚构函数加上virtual

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>

class BasicClass {
public:
virtual ~BasicClass() {
std::cout << "BasicClass destructor!\n";
}
};

class DerivedClass: public BasicClass {
public:
~DerivedClass() {
std::cout << "DerivedClass destructor!\n";
}
};

int main(void){

class BasicClass* p_class = new DerivedClass();

// 如果 BasicClass 的析构函数没有 virtual 关键字
// 那么执行 delete 后只会执行 BasicClass 的析构函数
// 也就是说,只释放了基类内存,而派生类的内存没有被释放掉
delete p_class;

return 0;
}
阅读全文 »

编译器默认会为一个类提供(如果它们需要被使用的话):

  • 默认构造函数:如果类编写了构造函数,则编译器就不会自动提供默认构造函数了
    • 使用 default 显示声明也可以在有其他构造函数的情况下,让编译器产生默认构造函数
  • 拷贝构造函数:没有移动函数显示定义时,拷贝函数才会被隐式的创建。其单纯地将每一个数据成员进行拷贝
    • 如果数据成员中的对象具有它自己的拷贝构造函数,则也会调用它
  • 拷贝赋值函数:单纯地将每一个数据成员进行拷贝
    • 如果数据成员中的对象具有它自己的拷贝赋值函数,则也会调用它
  • 析构函数:隐式的noexcept
  • 移动构造函数:当没有显示定义移动、拷贝、析构函数时,默认移动函数才会被隐式的创建
  • 移动赋值函数

生成的特殊函数是隐含的publicinline的,但大部分情况下都是非虚函数。 > 只有当一个类继承自基类,基类的析构函数是虚函数时,生成派生类的析构函数才也是虚函数

阅读全文 »

要养成好的习惯:永远在使用对象之前先将它初始化。 - 对于内置类型,在定义时就初始化 - 对于类类型,在构造函数初始值列表中按照声明顺序初始化 + 类类型中的私有变量是内置类型时,也可以在声明时初始化,这样可以避免初始值列表过长。 + 如果期望类类型中的对象以默认构造函数初始化时,那也可以不用放在初始值列表中

阅读全文 »