我的C++知识点学习笔记。
整理中
C++ Hello World
C++和C的区别
- C++是面向对象的语言,C是面向过程的语言;
- C++引入
new/delete
运算符,取代了C中的malloc/free
库函数; - C++引入引用的概念,而C中没有;
- C++引入类的概念,而C中没有;
- C++引入函数重载的特性,而C中没有;
面向对象的三大特征
- 封装:封装是面向对象方法的一个重要原则,就是把对象的属性和服务结合成一个独立的系统单位,并尽可能隐蔽对象的内部细节;
- 继承:特殊类的对象用于其一般类的全部属性与服务,称作特殊类对一般类的继承;
-
多态:多态性是指在一般类中定义的属性或行为,被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为;
- 静态多态:编译时的多态,绑定工作在编译连接阶段完成,称为静态绑定,e.g. 函数重载(包括运算符重载);
- 动态多态:运行时的多态,绑定工作在程序运行阶段完成,称为动态绑定,e.g. 虚函数;
C++关键字
const
关键字
符号常量
const value_type variable_name = variable_value;
- 符号常量在使用之前一定要首先声明:
- 符号常量不能被赋值:
常对象
const class_name object_name;
- 常对象必须进行初始化,而且不能被更新;
- 在声明常对象时,把
const
关键字放在类型名后面也是允许的,不过人们更习惯于把const
写在前面; - 基本数据类型的常量也可看作一种特殊的常对象,因此,后面将不再对基本数据类型的常量和类类型的常对象加以区分;
用const
修饰的类成员
常成员函数
value_type function_name(variables) const;
const
是函数类型的一个组成部分,因此在函数的定义部分也要带const
关键字;- 如果将一个对象声明为常对象,则通过该常对象只能调用它的常成员函数,而不能调用其他成员函数(这就是C++从语法机制上对常对象的保护,也是常对象唯一的对外接口方式);
- 无论是否通过常对象调用常成员函数,在常成员函数调用期间,目的对象都被视同为常对象,因此常对象函数不能更新目的对象的数据成员,也不能针对目的对象调用该类中没有用
const
修饰的成员函数(这就保证了在常成员函数中不会更改目的对象的数据成员的值); -
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;
- 类的成员数据也可以是常量;
- 常数据成员只能通过初始化列表来获得初值;
- 静态常数据成员在类外说明和初始化;
常引用
const value_type& reference_name;
- 常引用所引用的对象不能被更新;
- 如果用常引用作形参,便不会意外地发生对实参的更改;
- 对于在函数中无须改变其值的参数,不宜使用普通引用方式传递,因为那会使得常对象无法被传入,采用传值方式或传递常引用的方式可避免这一问题;
- 对于大对象来说,传值耗时较多,因此传递常引用为宜;
- 复制构造函数的参数一般也宜采用常引用传递;
- 在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
}
- 内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处;
- 对于一些功能简单、规模较小又使用频繁的函数,可以设计为内联函数;
new
关键字
static
关键字
静态数据成员
- 如果某个属性为整个类所共有,不属于任何一个具体对象,则采用
static
关键字来声明为静态成员; - 类属性是描述类的所有对象共有特征的一个数据项,对于任何对象实例,它的属性值是相同的;
- 静态数据成员具有静态生存期;
- 由于静态数据成员不属于任何一个对象,因此可以通过类名对它进行访问,一般的用法是
类名::标识符
; - 在类的定义中仅仅对静态数据成员进行引用性声明,必须在命名空间作用域的某个地方使用类名限定定义性声明,这时也可以进行初始化(在类中声明,在类外初始化);
静态函数成员
static value_type function_name(variables);
- 静态函数成员可以直接访问该类的静态数据和函数成员;
- 而访问非静态成员,必须通过对象名;
this
关键字
typedef
关键字
typedef type_name aliases
- 用于将一个标识符声明成某个数据类型的别名,然后将这个标识符当做数据类型使用;
- 新类型名表中可以有多个标识符,它们之间以逗号分隔;
using
关键字
virtual
关键字
虚函数
虚函数是动态绑定的基础。虚函数必须是非静态的成员函数。虚函数经过派生之后,在类族中就可以实现运行过程中的多态。
一般虚函数成员
virtual value_type function_name(variables);
- 虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候;
-
运行过程中的多态需要满足三个条件:
- 赋值兼容原则;
- 虚函数;
- 由成员函数来调用或者是通过指针、引用来访问虚函数;
- 虚函数一般不声明为内联函数,因为对虚函数的调用需要动态绑定,而对内联函数的处理是静态的,所以虚函数一般不能以内联函数处理。但将虚函数声明为内联函数也不会引起错误;
虚析构函数
virtual ~ClassName();
纯虚函数和抽象类
纯虚函数
virtual value_type function_name(variables) = 0;
抽象类
- 带有纯虚函数的类是抽象类;
- 抽象类不能实例化;
区别辨析
结构体struct
和类class
- 结构体是一种特殊形态的类,它和类一样,可以有自己的数据成员和函数成员,可以有自己的构造函数和析构函数,可以控制访问权限,可以继承,支持包含多态等,二者定义的语法形式也几乎一样;
-
结构体和类的唯一区别在于,结构体和类具有不同的默认访问控制属性:
- 在类中,对于未指定访问控制属性的成员,其访问控制属性为私有类型
private
; - 在结构体中,对于未指定任何访问控制属性的成员,其访问控制属性为公有类型
public
;
- 在类中,对于未指定访问控制属性的成员,其访问控制属性为私有类型
- C++引入结构体是为了保持和C程序的兼容性;
结构体struct
和联合体union
- 联合体是一种特殊形态的类,它可以有自己的数据成员和函数成员,可以有自己的构造函数和析构函数,可以控制访问权限;
- 与结构体一样,联合体也是从C语言继承而来的,因此它的默认访问控制属性也是公共属性的;
- 联合体的全部数据成员共享同一组内存单元;
-
联合体在存储时具有两种存储形式,具体采用哪种存储形式由处理器决定:
- 大端模式(big endian):高位字节保存在内存中的低位地址,低位字节保存在内存中的高位地址,这种存储方式符合人的正常思维习惯;
- 小端模式(little endian):低位字节保存在内存中的低位地址,高位字节保存在内存中的高位地址,这种存储方式有利于计算机处理;
#define
和const
const
和constexpr
size_t
和int
typedef unsigned int size_t; // 32位
typedef unsigned long size_t; // 64位
与int
固定四个字节不同,size_t
的取值范围是目标平台下最大可能的数组尺寸,一些平台下size_t
的范围小于int
的正数范围,又或者大于unsigned int
,使用int
既有可能浪费,又有可能范围不够大。
NULL
和nullptr
为解决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.
关于auto
和decltype
auto
:让编译器在编译期就推导出变量的类型,可以通过=
右侧的类型推导出变量的类型;decltype
:相对于auto
用于推导变量类型,而decltype
则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算;
constexpr
关键字
default
关键字
delete
关键字
for
循环
基于范围的for
循环:
vector<int> vec;
for (int i : vec) {}
string str;
for (char c : str) {}
关于智能指针
使用时需要包含头文件:
#include <memory>
std::auto_ptr
:自动指针,C++11弃用,C++17移出;std::shared_ptr
:共享指针;std::weak_ptr
:弱共享指针;std::unique_ptr
:独享指针;
共享指针std::shared_ptr
-
初始化方法,以指向
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);
-
共享指针辅助构造函数
make_shared()
原型声明:template <class T, class... Args> shared_ptr<T> make_shared(Args&&... args);
-
常用成员函数原型声明:
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
-
初始化方法,以指向
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::unique_ptr<int> p3(std::move(p2)); std::unique_ptr<int> p3 = std::move(p2);
-
独享指针禁止拷贝构造,拷贝构造函数原型声明:
// 使用delete关键字禁止编译器使用拷贝构造函数 std::unique_ptr(const std::unique_ptr&) = delete; // 拷贝构造无法编译通过 // std::unique_ptr<int> p4(p3); // std::unique_ptr<int> p4 = p3;
-
独享指针辅助构造函数
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)...)); }
-
常用成员函数原型声明:
element_type* get() const noexcept; // 返回指针 element_type* release() noexcept; // 返回指针,重置指针为空指针,释放对原内存的所有权,但是不释放原内存 void reset(element_type* p) noexcept; // 重置指针为指定指针,释放原内存 void swap(std::unique_ptr& x) noexcept; // 交换指针内容
参考
- 《C++语言程序设计》
- cplusplus
- 校招C++大概学习到什么程度?-程序员内功修炼的回答-知乎
- C++教程-菜鸟教程
- C++入门教程-C语言中文网
- C++11教程-C语言中文网
- extern “C”-C语言中文网
- 共用体的大端模式和小端模式1-CSDN博客
- 共用体的大端模式和小端模式2-CSDN博客
- size_t和int1-CSDN博客
- size_t和int2-CSDN博客
- NULL和nullptr-CSDN博客
- 嵌套模板中的»1-Stack Overflow
- 嵌套模板中的»2-Stack Overflow
- C++11新特性,所有知识点都在这了!-程序喵大人的文章-知乎
- default和delete-CSDN博客
- c++是否应避免使用普通指针,而使用智能指针(包括shared,unique,weak)?-张小方的回答-知乎
- 现代C++:一文读懂智能指针-FOCUS的文章-知乎
- C++智能指针-小小将的文章-知乎
- C++11 shared_ptr智能指针-C语言中文网
- C++11 unique_ptr智能指针详解-C语言中文网
- C++11 weak_ptr智能指针-C语言中文网
- std::unique_ptr release的使用-博客园