C++知识点学习笔记

zxl19 2021-09-25

我的C++知识点学习笔记。

整理中

C++ Hello World

C++和C的区别

  1. C++是面向对象的语言,C是面向过程的语言;
  2. C++引入new/delete运算符,取代了C中的malloc/free库函数;
  3. C++引入引用的概念,而C中没有;
  4. C++引入类的概念,而C中没有;
  5. C++引入函数重载的特性,而C中没有;

面向对象的三大特征

  1. 封装:封装是面向对象方法的一个重要原则,就是把对象的属性和服务结合成一个独立的系统单位,并尽可能隐蔽对象的内部细节;
  2. 继承:特殊类的对象用于其一般类的全部属性与服务,称作特殊类对一般类的继承;
  3. 多态:多态性是指在一般类中定义的属性或行为,被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为;

    • 静态多态:编译时的多态,绑定工作在编译连接阶段完成,称为静态绑定,e.g. 函数重载(包括运算符重载);
    • 动态多态:运行时的多态,绑定工作在程序运行阶段完成,称为动态绑定,e.g. 虚函数;

C++关键字

const关键字

符号常量

const value_type variable_name = variable_value;
  1. 符号常量在使用之前一定要首先声明:
  2. 符号常量不能被赋值:

常对象

const class_name object_name;
  1. 常对象必须进行初始化,而且不能被更新;
  2. 在声明常对象时,把const关键字放在类型名后面也是允许的,不过人们更习惯于把const写在前面;
  3. 基本数据类型的常量也可看作一种特殊的常对象,因此,后面将不再对基本数据类型的常量和类类型的常对象加以区分;

const修饰的类成员

常成员函数
value_type function_name(variables) const;
  1. const是函数类型的一个组成部分,因此在函数的定义部分也要带const关键字;
  2. 如果将一个对象声明为常对象,则通过该常对象只能调用它的常成员函数,而不能调用其他成员函数(这就是C++从语法机制上对常对象的保护,也是常对象唯一的对外接口方式);
  3. 无论是否通过常对象调用常成员函数,在常成员函数调用期间,目的对象都被视同为常对象,因此常对象函数不能更新目的对象的数据成员,也不能针对目的对象调用该类中没有用const修饰的成员函数(这就保证了在常成员函数中不会更改目的对象的数据成员的值);
  4. const关键字可以用于对重载函数的区分,如果仅以const关键字为区分对成员函数重载,那么通过非const的对象调用该函数,两个重载的函数都可以与之匹配,这时编译器将选择最近的重载函数——不带const关键字的函数:

     T& operator[](const std::size_t i) { return data_[i]; }
     const T& operator[](const std::size_t i) const { return data_[i]; }
    
常数据成员
const value_type variable_name;
static const value_type variable_name;
  1. 类的成员数据也可以是常量;
  2. 常数据成员只能通过初始化列表来获得初值;
  3. 静态常数据成员在类外说明和初始化;

常引用

const value_type& reference_name;
  1. 常引用所引用的对象不能被更新;
  2. 如果用常引用作形参,便不会意外地发生对实参的更改;
  3. 对于在函数中无须改变其值的参数,不宜使用普通引用方式传递,因为那会使得常对象无法被传入,采用传值方式或传递常引用的方式可避免这一问题;
  4. 对于大对象来说,传值耗时较多,因此传递常引用为宜;
  5. 复制构造函数的参数一般也宜采用常引用传递;
  6. 在32位处理器上,小于4字节的数据类型建议传值,在64位处理器上,小于8字节的数据类型建议传值;

delete关键字

enum关键字

extern关键字

外部变量和外部函数

C++和C的混合编程

使用extern "C"实现C++和C的混合编程

#ifdef __cplusplus
extern "C" {
#endif

void display();

#ifdef __cplusplus
}
#endif

friend关键字

inline关键字

inline value_type function_name(variables) {
    // implementation
}
  1. 内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处;
  2. 对于一些功能简单、规模较小又使用频繁的函数,可以设计为内联函数;

new关键字

static关键字

静态数据成员

  1. 如果某个属性为整个类所共有,不属于任何一个具体对象,则采用static关键字来声明为静态成员;
  2. 类属性是描述类的所有对象共有特征的一个数据项,对于任何对象实例,它的属性值是相同的;
  3. 静态数据成员具有静态生存期;
  4. 由于静态数据成员不属于任何一个对象,因此可以通过类名对它进行访问,一般的用法是类名::标识符
  5. 在类的定义中仅仅对静态数据成员进行引用性声明,必须在命名空间作用域的某个地方使用类名限定定义性声明,这时也可以进行初始化(在类中声明,在类外初始化);

静态函数成员

static value_type function_name(variables);
  1. 静态函数成员可以直接访问该类的静态数据和函数成员;
  2. 而访问非静态成员,必须通过对象名;

this关键字

typedef关键字

typedef type_name aliases
  1. 用于将一个标识符声明成某个数据类型的别名,然后将这个标识符当做数据类型使用;
  2. 新类型名表中可以有多个标识符,它们之间以逗号分隔;

using关键字

virtual关键字

虚函数

虚函数是动态绑定的基础。虚函数必须是非静态的成员函数。虚函数经过派生之后,在类族中就可以实现运行过程中的多态。

一般虚函数成员
virtual value_type function_name(variables);
  1. 虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候;
  2. 运行过程中的多态需要满足三个条件:

    • 赋值兼容原则;
    • 虚函数;
    • 由成员函数来调用或者是通过指针、引用来访问虚函数;
  3. 虚函数一般不声明为内联函数,因为对虚函数的调用需要动态绑定,而对内联函数的处理是静态的,所以虚函数一般不能以内联函数处理。但将虚函数声明为内联函数也不会引起错误;
虚析构函数
virtual ~ClassName();

纯虚函数和抽象类

纯虚函数
virtual value_type function_name(variables) = 0;
抽象类
  1. 带有纯虚函数的类是抽象类;
  2. 抽象类不能实例化;

区别辨析

结构体struct和类class

  1. 结构体是一种特殊形态的类,它和类一样,可以有自己的数据成员和函数成员,可以有自己的构造函数和析构函数,可以控制访问权限,可以继承,支持包含多态等,二者定义的语法形式也几乎一样;
  2. 结构体和类的唯一区别在于,结构体和类具有不同的默认访问控制属性:

    • 在类中,对于未指定访问控制属性的成员,其访问控制属性为私有类型private
    • 在结构体中,对于未指定任何访问控制属性的成员,其访问控制属性为公有类型public
  3. C++引入结构体是为了保持和C程序的兼容性;

结构体struct和联合体union

  1. 联合体是一种特殊形态的类,它可以有自己的数据成员和函数成员,可以有自己的构造函数和析构函数,可以控制访问权限;
  2. 与结构体一样,联合体也是从C语言继承而来的,因此它的默认访问控制属性也是公共属性的;
  3. 联合体的全部数据成员共享同一组内存单元;
  4. 联合体在存储时具有两种存储形式,具体采用哪种存储形式由处理器决定:

    • 大端模式(big endian):高位字节保存在内存中的低位地址,低位字节保存在内存中的高位地址,这种存储方式符合人的正常思维习惯;
    • 小端模式(little endian):低位字节保存在内存中的低位地址,高位字节保存在内存中的高位地址,这种存储方式有利于计算机处理;

#defineconst

constconstexpr

size_tint

typedef unsigned int size_t;    // 32位
typedef unsigned long size_t;   // 64位

int固定四个字节不同,size_t的取值范围是目标平台下最大可能的数组尺寸,一些平台下size_t的范围小于int的正数范围,又或者大于unsigned int,使用int既有可能浪费,又有可能范围不够大。

NULLnullptr

为解决NULL代指空指针存在的二义性问题,在C++11中引入nullptr关键字来代指空指针:

NULL        // 0
nullptr     // 空指针,C++11引入

C++11

关于嵌套模板中相邻的右尖括号

C++11支持嵌套模板中相邻的右尖括号写作>>

vector<vector<int>>

早期C++标准要求嵌套模板中相邻的右尖括号写作> >

vector<vector<int> >

其原因是为了避免与输入流运算符>>混淆,否则会报错:

'>>' should be '> >' within a nested template argument list.

关于autodecltype

  1. auto:让编译器在编译器就推导出变量的类型,可以通过=右边的类型推导出变量的类型;
  2. decltype:相对于auto用于推导变量类型,而decltype则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算;

constexpr关键字

default关键字

delete关键字

for循环

基于范围的for循环:

vector<int> vec;
for (int i : vec) {}
string str;
for (char c : str) {}

关于智能指针

使用时需要包含头文件:

#include <memory>
  1. std::auto_ptr:自动指针,C++11弃用,C++17移出;
  2. std::shared_ptr:共享指针;
  3. std::weak_ptr:弱共享指针;
  4. std::unique_ptr:独享指针;

共享指针std::shared_ptr

  1. 初始化方法,以指向int类型的共享指针为例:

     // 空共享指针,初始引用计数为0
     std::shared_ptr<int> p1;
     std::shared_ptr<int> p1(nullptr);
     p1.reset(new int(10));
     // 赋值构造
     std::shared_ptr<int> p2(new int(10));
     std::shared_ptr<int> p2 = std::make_shared<int>(10);    // 辅助构造函数
     // 拷贝构造
     std::shared_ptr<int> p3(p2);
     std::shared_ptr<int> p3 = p2;
     // 移动构造
     std::shared_ptr<int> p4(std::move(p3));
     std::shared_ptr<int> p4 = std::move(p3);
    
  2. 共享指针辅助构造函数make_shared()原型声明:

     template <class T, class... Args>
     shared_ptr<T> make_shared(Args&&... args);
    
  3. 常用成员函数原型声明:

     void swap(std::shared_ptr& x) noexcept;     // 交换指针内容
     void reset() noexcept;                      // 重置指针为空指针
     template <class U> void reset(U* p);        // 重置指针为指定指针,要求U类型指针能够隐式转换为T类型指针
     element_type* get() const noexcept;         // 返回指针
     long int use_count() const noexcept;        // 返回引用计数
     bool unique() const noexcept;               // 判断是否独享
    

独享指针std::unique_ptr

  1. 初始化方法,以指向int类型的独享指针为例:

     // 空独享指针
     std::unique_ptr<int> p1;
     std::unique_ptr<int> p1(nullptr);
     p1.reset(new int(10));
     // 赋值构造
     std::unique_ptr<int> p2(new int(10));
     std::unique_ptr<int> p2 = std::make_unique<int>(10);    // 辅助构造函数
     // 移动构造
     std::shared_ptr<int> p3(std::move(p2));
     std::shared_ptr<int> p3 = std::move(p2);
    
  2. 独享指针禁止拷贝构造,拷贝构造函数原型声明:

     // 使用delete关键字禁止编译器使用拷贝构造函数
     std::unique_ptr(const std::unique_ptr&) = delete;
     // 拷贝构造无法编译通过
     // std::unique_ptr<int> p4(p3);
     // std::unique_ptr<int> p4 = p3;
    
  3. 独享指针辅助构造函数make_unique()原型声明:

     // C++14引入
     template <class T, class... Args>
     unique_ptr<T> make_unique(Args&&... args);
    
     // 在C++11中的实现
     template <typename T, typename... Args>
     std::unique_ptr<T> make_unique(Args&&... args) {
         return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
     }
    
  4. 常用成员函数原型声明:

     element_type* get() const noexcept;         // 返回指针
     element_type* release() noexcept;           // 返回指针,重置指针为空指针,释放对原内存的所有权,但是不释放原内存
     void reset(element_type* p) noexcept;       // 重置指针为指定指针,释放原内存
     void swap(std::unique_ptr& x) noexcept;     // 交换指针内容
    

参考

  1. 《C++语言程序设计》
  2. cplusplus
  3. 校招C++大概学习到什么程度?-程序员内功修炼的回答-知乎
  4. C++教程-菜鸟教程
  5. C++入门教程-C语言中文网
  6. C++11教程-C语言中文网
  7. extern “C”-C语言中文网
  8. 共用体的大端模式和小端模式1-CSDN博客
  9. 共用体的大端模式和小端模式2-CSDN博客
  10. size_t和int1-CSDN博客
  11. size_t和int2-CSDN博客
  12. NULL和nullptr-CSDN博客
  13. 嵌套模板中的»1-Stack Overflow
  14. 嵌套模板中的»2-Stack Overflow
  15. C++11新特性,所有知识点都在这了!-程序喵大人的文章-知乎
  16. default和delete-CSDN博客
  17. c++是否应避免使用普通指针,而使用智能指针(包括shared,unique,weak)?-张小方的回答-知乎
  18. 现代C++:一文读懂智能指针-FOCUS的文章-知乎
  19. C++智能指针-小小将的文章-知乎
  20. C++11 shared_ptr智能指针-C语言中文网
  21. C++11 unique_ptr智能指针详解-C语言中文网
  22. C++11 weak_ptr智能指针-C语言中文网
  23. std::unique_ptr release的使用-博客园