计算机基础知识总结(语法篇)
成员变量初始化
构造函数内部初始化和初始化列表
初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。
- 对于内置类型,如int, float等,使用初始化列表和在构造函数体内初始化差别不大;
- 对于类类型来说,使用初始化列表会减少调用默认构造函数的次数,更加高效。
|
|
这段代码输出:
|
|
如果不使用初始化列表,使用17行构造函数内部赋值的方式,会输出:
|
|
除了性能问题之外,有时候初始化列表是不可或缺的,以下几种情况必须使用初始化列表:
-
类的const成员
-
引用类型
const对象或引用只能初始化但是不能赋值,构造函数的函数体内只能做赋值而不是初始化。因此初始化const对象或引用的唯一机会是构造函数函数体之前的初始化列表中。
-
没有默认构造函数的类类型
|
|
以上代码无法通过编译,因为B的构造函数中a=a1
这一行会先调用A的默认构造函数来初始化a1,由于A没有默认的构造函数,所以无法执行,故而编译错误。
如果基类没有默认构造函数,派生类必须在其初始化列表中显示调用基类的构造函数。
构造函数调用顺序
- 调用基类构造函数,调用顺序按照他们的继承时声明的顺序。
- 调用内嵌成员对象的构造函数,调用顺序按照他们在类中声明的顺序。
- 调用派生类自己的构造函数体中的内容。
析构函数的调用顺序相反。
更详细的:构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。
- 初始化阶段:所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中。
- 计算阶段:一般用于执行构造函数体内的赋值操作。
所以,初始化列表总是先于构造函数体执行:基类初始化列表 → 基类的构造函数 → 派生类初始化列表 → 派生类的构造函数。
成员变量初始化顺序
成员是按照他们在类中出现的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的。
|
|
const和constexpr
constexpr关键字是C++11新增的,其作用包括:
常量分为编译期常量、运行期常量。 编译期常量在编译阶段就可以确定其值,并将其结果展开到使用的地方,一般存放在rodata段。 运行期常量本质上是只读的变量(存放在栈区),编译时无法确定其值;运行时,无法修改其值。
- const 可以定义编译期常量,也可以定义运行期常量;
- constexpr 只能定义编译期常量。
1 2 3 4 5 6 7
const int a = 10; // 编译期常量,存放在静态存储区的rodata段 int main() { int b = 20; // 栈 const int c = b; // 栈(运行期常量) // constexpr int c = b; Error: 因为b是一个普通变量,必须到运行期才能确定,constexpr无法修饰运行期常量。 }
普通函数必须在运行时才能执行,进而计算出结果。而常量表达式函数要求函数在编译期就计算出结果,运行时直接使用结果。也就是说将函数的执行从运行阶段转移到编译阶段,提升程序运行效率。 只需要在函数返回值类型前面加上constexpr关键字即可定义常量表达式函数,我们必须使用constexpr修饰的编译期常量来保存常量表达式函数的结果,否则常量表达式函数仍然会在运行期执行。
1 2 3 4 5 6 7 8
constexpr int my_sum(int n) { if (n == 1) return 1; return n + my_sum(n - 1); } void test() { constexpr int a = my_sum(3); }
编译期常量对象的任何计算都在编译期完成。定义编译期常量对象,有以下几点要求:
- 构造函数使用constexpr修饰,必须使用初始化列表对成员进行初始化;
- 对象调用的成员函数必须使用constexpr修饰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
class Box { public: // 构造函数使用constexptr修饰,并且使用初始化列表对成员进行初始化。这就保证了对象成员m_l、m_w、m_h在编译期确定其值。 constexpr Box(int l, int w, int h) : m_l(l), m_w(w), m_h(h) {} // get_volume函数被定义为了常量表达式成员函数,调用该函数则会在编译阶段计算出结果。 constexpr int get_volume() const { return m_l * m_w * m_h; } // get_sum函数则是普通函数,该函数必须声明为const函数,否则无法被常量对象调用 int get_sum() const { return (m_l + m_w + m_h); } public: int m_l, m_w, m_h; }; void test() { constexpr Box box(10, 20, 30); // 程序编译期间调用常量表达式构造函数 constexpr int volume = box.get_volume(); // 在编译期计算出函数的执行结果,并赋值给编译期常量volume int my_sum = box.get_sum(); // 在运行期进行执行 }
单例模式
单例模式是一种设计模式,它确保一个类只有一个实例,并且提供一个全局访问点来访问它。 单例模式的实现一般需要将构造函数、析构函数私有化,禁用拷贝构造函数和赋值运算符,将成员变量和成员函数都设置成静态类型。
懒汉单例模式实现
- 最简单的懒汉模式(在全局访问入口中声明静态变量。局部静态变量在C++11后也是线程安全的)
|
|
- 加锁版本的懒汉单例模式
|
|
饿汉单例模式实现
饿汉单例模式天生线程安全,因为在main函数前已经初始化
|
|