• Cherno C++视频课程-学习笔记


    P16、P17 C++指针和引用

    待补充

    P19 C++类和结构体的对比

    1.语法区别:仅有内部成员可见性的区别。类成员默认是private,而结构体成员默认是public。
    2.常用方式:

    • 结构体:常用来定义新的变量类型。也用来保持与C语言的兼容性。
    • 类:常用于继承场景。

    P21 C++中的外部static

    1.外部static指位于类或结构体定义的外部。
    2.通常用于修饰全局变量、类或结构体的定义。
    3.作用:主要用于链接阶段,限制全局变量、类或结构体仅在本编译单元(通常指当前.cc源文件)可见,而在其他编译单元中不可见。
    4.使用建议:如果不是明确需要全局可见性,最好加上static修饰。

    P22 C++类和结构体中的内部static

    1.内部static指用于修饰类或结构体中的成员变量或成员函数。
    2.作用:通过类或结构体可以定义多个对象,但是用static修饰的成员在内存中仅有一份。
    3.使用限制:

    • static成员变量可以通过类名访问,不能通过对象访问。
    • static成员函数仅能处理static成员变量,不能处理非static的成员变量。

    P23 C++中的局部静态local static

    1.局部静态变量通常指static修饰函数内定义的局部变量。
    2.作用:使局部变量的生存周期扩大为整个程序的生成周期,但是变量的作用域不变。

    P24 C++枚举

    1.枚举可以在类中定义
    2.类构成一个命名空间。但是枚举不是命名空间,可以直接访问枚举成员,而不必加枚举类型名。

    P25 C++构造函数

    1.定义:实例化类对象时,自动被调用的函数。
    2.在C++中默认构造函数等于空函数,什么都不做。Java的默认构造函数会将成员变量初始化。
    3.阻止类实例化对象的方法:显示定义一个默认构造函数(没有参数),并且仅有这一个构造函数,并且将该构造函数设置为private。

    P26 C++析构函数

    1.定义:在类对象被销毁时,自动被调用的函数。
    2.适用范围:

    • 栈对象:因作用域结束而被删除时。
    • 堆对象:因调用delete函数显示删除堆对象时。

    P27 C++继承

    1.在子类中,可以重写父类中的方法。但是相比于不重写父类中的方法,需要占用额外的内存,因为子类需要维护一个虚函数表。

    P28 C++虚函数

    1.定义:使用virtual关键字修饰类中成员函数的定义。通常用于修饰基类中的成员函数,因为基类中的成员函数可能被子类覆写override。
    2.作用:编译时会告知编译器生成虚函数表,主要用于实现多态。
    3.C++11引入的override关键字:

    • 应用位置:用于子类中覆写函数定义的参数列表之后。
    • 作用:仅用于合法性检查,比如基类中被覆写的函数是否被virtual修饰、子类中被override修饰的方法在基类中是否存在等。

    4.虚函数的开销:

    • 内存开销:虚函数表,以及基类中指向虚函数表的指针。
    • 运行开销:需要遍历虚函数表,来确定要映射到覆写的哪个函数。

    P29 C++接口(纯虚函数)

    1.纯虚函数:类的成员方法,只声明了函数,没有实现函数体。(通常将函数体{…}替换为 =0;)
    2.接口:只包含纯虚函数的类。

    P30 C++可见性

    1.可见性修饰符:

    • private:只有当前类中成员,能够访问被private修饰的成员。
    • protected:只有当前类以及当前类的子类中的成员,能够访问被protected修饰的成员。
    • public:在任何位置都能访问被public修饰的成员。

    P31 C++数组

    1.在堆上申请数组内存

    int *arr = new int[5];
    delete[] arr;
    
    • 1
    • 2

    2.C++11引入了std:array,优点:

    • 越界检查
    • 记录数组元素个数
    #include 
    std::array arr;	// 指明数组元素的类型和个数
    for (int i = 0; i < arr.size(); i++) {arr[1] = 10;}
    
    • 1
    • 2
    • 3

    3.C++数组与C数组的不同之处

    // 该数组定义在C++中合法,但是在C中非法
    static const int size = 5;
    int arr[size];
    
    • 1
    • 2
    • 3

    P32 C++字符串

    #include 
    #include 
    
    // void Print(std::string name) // 类对象作为函数参数时,是值拷贝的。建议使用引用作为类对象的参数
    void Print(const std::string& name)
    {
    	std::cout << name <
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    P33 C++字符串字面量

    1.不同的字符类型:

    const char *name1 = "Cherno";
    const char *name2 = u8"Cherno";
    const wchar_t *name3 = L"Cherno";		// wchar_t 1至4字节
    const char16_t *name4 = u"Cherno";		// 2字节
    const char32_t *name5 = U"Cherno";		// 4字节
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.C++14引入的std::string_literals

    using namespace std::string_literals;
    std::string name = "hello"s + "world"; // 在C++14中合法,后缀s表示将“hello”转换为std::string_literals类型;在C++11中非法
    std::string name = std::string("hello") + "world"; // 在C++14中合法;在C++11中合法
    
    • 1
    • 2
    • 3

    3.忽略字符串中的转移字符

    std::string name = R("line1
    line2
    line3");		// 前缀R表示忽略字符串中的转义字符
    
    • 1
    • 2
    • 3

    P34 C++中的CONST

    1.const修饰符在变量定义时的位置

    const int *var = new int;	// 不能修改var指针,指向的内存;但是可以修改var的内容,即var可以指向新的内存地址。
    int const * var = new int;	// 同上
    int * const var = new int;	// 可以修改var指针,指向的内存;但是不能修改var的内容,即var不能指向新的内存地址。
    
    • 1
    • 2
    • 3

    2.const用于C++类中的成员函数的定义

    class Entity {
    	private:
    		int m_X, *m_Y;
    		mutable int var;
    	public:
    		int GetX1() const {	// 该const表示,在方法定义中不能修改类的成员变量。
    			// m_X = 2;          // 修改m_X会报错。
    			return m_X;
    		}
    		int GetX2() const {
    			var = 2;				// 被mutable修饰的成员变量,可以在const修饰的成员函数中修改。
    			return m_X;
    		}
    		const int * const GetY1() const {	// 第一个const:返回的指针变量的内容不能被修改;第2个const:返回的指针变量本身不能被修改。
    			return m_Y;
    		}
    };
    
    void PrintEntity(const Entity &e) {	
    	std::cout << e.GetX() << std::endl;    // 变量e的引用被const修饰,e的成员函数GetX()也必须被const修饰,否则编译不通过。
    }
    Entity e;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    P36 C++的成员初始化列表

    class Example
    {
    	private:
    		int num;
    	public:
    		Example()
    		{
    			std::cout << "once" << std::endl;
    		}
    		
    		Example(int n)
    		{
    			num = n;
    			std::cout << "twice with " << n << std::endl;
    		}
    }
    
    class Entity
    {
    	private:
    		std::string m_Name;
    		int m_Age;
    		Example  m_Example;
    	public:
    		Entity(std::string &name, int age)
    		{
    			m_Name = name;
    			m_Age = age;
    			
    			// 结论:将会打印“once”和“twice with 8”。原因:成员变量m_Example定义时,将调用无参的构造函数。
    			m_Example = Example(8);
    			// do other thingsl
    		}
    
    		// 结论:将会打印“twice with 8”。
    		// 结论2:不仅是代码风格的问题! 成员初始化列表减少了类成员对象的默认构造函数的调用,提高了性能。
    		Entity() : m_Name("lfc"), m_Age(18), m_Example(Example(8))
    		{
    			// do other thingsl
    		}
    }
    
    结论:尽量用成员初始化列表,对性能提升有帮助。
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    P37 C++的三元运算符

    方式1:
    int age = 18;
    std::string desp;	// 调用默认构造函数,创建了desp对象
    if (age > 18) {
    	desp  = "man";		// 再次调用std::string的构造函数,并创建了新的对象。
    } else {
    	desp = "child";
    }
    
    方式2:
    int age = 18;
    std::string desp = age > 18 ? "man" : "child"; 		// 仅调用了一次std::string的构造函数。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    P38 创建并初始化C++对象

    // 在堆上分配
    Entity *e = new Entity("Cherno");
    delete e;
    
    // 在栈上分配
    Entity e2 = Entity("Cherno");
    Entity e3("Cherno");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    P39 C++ new关键字

    // 数组的申请和释放
    int *arr = new int[50];
    delete[]  arr;
    
    // 类对象的申请和释放
    Entity *e = new Entity()	// 在堆上分配内存,并调用类的构造函数。
    delete e; 							调用析构函数,并释放堆内存。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    P40 C++隐式转换与explicit关键字

    class Entity {
    	private:
    		std:string m_Name;
    		int m_Age;
    	public:
    		Entity(std::string name) : m_Name(name), m_Age(-1) {}
    		Entity(std::string name) : m_Name(name), m_Age(-1) {}
    		// 在定义构造函数时,使用explicit关键字进行了声明。作用:与隐式转换相关,请看本节介绍。
    		// explicit Entity(std::string name) : m_Name(name), m_Age(-1) {} 
    };
    
    void PrintEntity(Entity &e) {
    	// print something
    }
    
    Entity e1 = "Cherno";  // 合法。使用了隐式构造函数
    Entity e2 = 22;  // 合法。使用了隐式构造函数
    PrintEntity(22);   // 合法。使用了隐式构造函数
    PrintEntity("Cherno");   // 不合法。隐式转换只能进行一次。此处期望将const char *类型的"Cherno"转换为std::string,然后再转换为Entity。需要进行
    两次转换,所以不合法。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    explicit关键字,可以禁用隐式构造函数。

    P41 C++运算符及其重载

    class Vector2 {
    	private:
    		float x;
    		float y;
    	public:
    		Vector2(float x, float y) : x(x), y(y) {}	// 构造函数
    		Vector2 Add(const Vector2 & other) {	// Add函数
    			return Vector2(x + other,x, y + other.y);
    		}
    		Vector2 operator+(const Vector2 & other) {	// 重载操作符“+”
    			return Vector2(x + other,x, y + other.y);
    		}
    };
    
    Vector2 v1(1.1, 2.2);
    Vector2 v2(3.3, 4.4);
    Vector2 v3 = v1.Add(&v2); // 调用Add函数,进行加法运算
    Vector2 v4 = v1 + v2;		   // 使用重载的操作符"+",进行加法运算
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    P43 C++的对象生存期(栈作用域生存期)

    class Entity {
    	public:
    		Entity() {
    			std::cout << "Constured Entity!" << std::endl;
    		}
    		~Entity() {
    			std::cout << "Constured Entity!" << std::endl;
    		}
    }
    
    class ScopedPtr {
    	private:
    		Entity *m_Ptr;
    	public:
    		SscopedPtr(Entity *ptr) : m_Ptr(ptr){} // 构造函数
    		~SscopedPtr(Entity *ptr) { // 析构函数
    			delete m_Ptr;
    		}
    }
    
    int main() {
    	{
    		ScopedPtr p = ScopedPtr(new Entity);	// 在该作用域内创建了局部变量p
    	} // 作用域退出时,局部变量p被释放,自动调用析构函数,进而将使用new创建Entity释放。
    	reutrn 0;
    }
    
    • 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

    作用域指针(智能指针):就是类对指针的包装。

    P44 C++的智能指针

    1.unique_ptr就是作用域指针。

    • 作用:作用域结束后,在该作用域内定义的unique_ptr所指向的内存,会被自动释放。
    • 注意:unique_ptr不能被复制。
    #include 
    class Entity {
    }
    int main()
    {
    	{
    		// 初始化unique_ptr智能指针的两种方法
    		std::unique_ptr e(new Entity);
    		std::unique_ptr e2 = std::make_unique();	 // 推荐方式!
      	    std::unique_ptr e3 = e2; // 非法!
    	} // 作用域退出时,e和e2被释放
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.shared_ptr共享指针。

    • 实现方式:取决于编译器和标准库。通常基于引用计数实现。
    #include 
    class Entity {
    }
    int main()
    {
    	std::shared_ptr e3;
    	{
    		// 初始化shared_ptr智能指针的两种方法
    		std::shared_ptr e(new Entity);
    		std::shared_ptr e2 = std::make_shared();	 // 推荐方式!
      	    e3 = e2; // 合法!
    	} // 作用域退出时,e被释放,e2没有被释放
    }     // main函数退出时,e2被释放
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.weak_ptr弱指针。不会增加引用计数。

    #include 
    class Entity {
    }
    int main()
    {
    	std::weak_ptr e1;
    	{
    		std::shared_ptr e2 = std::make_shared();	 // 推荐方式!
      	    e31= e2; // 合法!但是不会增加e2的引用计数。
    	} // 作用域退出时,e2没有被释放。此时e1指向无效的指针
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    P45 C++的复制与拷贝构造函数

    class String
    {
    	private:
    		char *m_Buffer;
    		unsigned int m_Size;
    	public:
    		String(const char *string) { // 普通构造函数
    			m_Size = strlen(string);
    			m_Buffer = new char[m_Size + 1];
    			memcpy(m_Buffer, string, m_Size);
    			m_Buffer[m_Size] = 0;
    		}
    		
    		//String(const String& other)  // c++提供的默认拷贝构造函数的实现示例,是一个浅拷贝的实现
    		//	: m_Buffer(other.m_Buff), m_Size(other.m_Size)
    		//{
    		//}
    
    		// 禁用拷贝构造函数
    		//String(const String& other) = delete;
    		
    		String(const String& other)  // 自定义深度拷贝的拷贝构造函数
    			: m_Size(other.m_Size)
    		{
    			m_Buffer = new char[m_Size + 1];
    			memcpy(m_Buffer, other.m_Buffer, m_Size);
    			m_Buffer[m_Size] = 0;
    		}
    		
    		~String() { // 析构函数
    			delete[] m_Buffer;
    		}
    
    		// String类的"[]"操作符重载函数
    		char& operator[](unsigned int index) {
    			return m_Buffer[index];
    		}
    		
    		// 通过friend关键字,声明友元函数,在该函数内可以访问String对象的私有成员。
    		friend std::ostream& operator<<(std::ostream& stream, const String& string);
    }
    
    // String类的"<<"操作符重载函数
    std::ostream& operator<<(std::ostream& stream, const String& string) {
    	stream << string.m_Buffer;	// 直接访问私有成员,因为已经将该函数声明为了友元函数。
    	return stream
    }
    
    int main() {
    	String string = "Cherno";
    	// 此处会调用String类的拷贝构造函数。C++提供默认拷贝构造函数,仅提供浅拷贝
    	// 禁用拷贝构造函数后,该复制操作是非法的
    	String second = string; 
    	second[2] = 'a'; // 调用String类的"[]"操作符重载函数
    	std::cin.get();
    }
    
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    结论:类对象作为函数参数时,尽量传递const引用,否则会调用拷贝构造函数。

    P46 C++的箭头操作符(重载)

    class Entity {
    	public:
    		void Print() {
    			std::cout << "hello Entity!" << std::endl;
    		}
    }
    
    class ScopePtr {
    Private:
    	Entity* m_Objl
    
    public:
    	ScopePtr(Entity* e) : m_Obj(e) {
    	}
    	~ScopePtr() {
    		delete m_Obj;
    	}
    	Entity* GetObject() {
    		return o_Obj;
    	}
    	// 重载了"->"操作符
    	Entity* operator->() {
    		return o_Obj;
    	}
    }
    
    int main() {
    	ScopePtr sp = new Entity();
    	sp.GetObject()->Print();
    	sp->Print(); // 该方式合法,因为->操作符被重载了
    }
    
    • 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
    • 29
    • 30
    • 31

    P47 C++中的动态数组 & P48 C++动态数组的优化

    #include 
    struct Vertex {
    	float x, y, z;
    }
    
    // 为Vertex类型定义<<操作符的重载函数
    std::ostream& operator<<(std::ostream& stream, const Vertex& vertex) {
    	stream << vertex.x << " , " << vertex.y << " , " << vertex.zl
    	return stream;
    }
    
    int main() {
    	std::Vector vert;
    	vert.push_back({1, 2, 3});	// 像动态数组vert中添加元素
    	vert.push_back({4, 5, 6});
    
    	// 遍历方式1
    	for (int i = 0; i < vert.size(); i++) {
    		std::cout << vert[i] <
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    P49 C++中使用库(静态链接)

    1.windows库的提供方式(linux可参考):

    libName-version-Platform/
    	dosc/   : 包含文档
    	include/  : 包含头文件
    	lib-mingw/  :  使用minggw编译的库文件
    	lib-mingw64/  :  使用minggw64编译的库文件
    	lib-vc2012/  :  使用vc2012编译的库文件
    	lib-vc2015/  :  使用vc2015编译的库文件
    		libName.lib  : 静态库
    		libName.dll  : 动态库
    		libNamedll.lib  : 动态库的符号表,链接到可执行文件时使用,但是.dll库仍是需要的。注意:动态链接时,该文件可不用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.C++源代码中,声明C语言的函数

    #include 
    // #include 
    
    extern "C" int glfwinit();
    
    int main() {
    	int ret = glfwinit();
    	return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    P53 C++的模板

    1.功能类似的函数重载,可以用模板代替

    #include 
    #include 
    
    void Print(int value) {
    	std::cout << value << std::endl
    }
    
    void Print(float value) {
    	std::cout << value << std::endl
    }
    
    void Print(std::string value) {
    	std::cout << value << std::endl
    }
    
    int main() {
    	Print(5);
    	Print(1.3);
    	Print("Hello Cherno");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.使用模板代替上例中重复的函数

    #include 
    #include 
    
    // 在函数的返回类型前,使用template关键字定义模板
    // typename和class在模板中作用是相同的,都用来定义类型符号
    // 在编译阶段,编译器会根据函数的调用,将模板函数展开为具体类型的函数。因此
    // 模板实际就是告诉编译器如何编码
    template
    void Print(T value) {
    	std::cout << value << std::endl
    }
    
    int main() {
    	Print(5);
    	Print(1.3);
    	Print("Hello Cherno");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 模板用于类中
    #include 
    #include 
    
    // 在使用class关键字定义类前,声明模板
    // int N: 与typename定义类型声明不同;此处的N是声明了一个int类型的符号,类似于宏。
    template
    class Array {
       private:
       	T m_Array[N];
       public:
       	int GetSize() const { return N; }
    }
    
    int main() {
       Array arr1;				// 创建包含5个元素的数组,元素类型是int。
       Array arr2; // 创建包含10个元素的数组,元素类型是std::string
    }
    
    注意:本节创建的"变长"数组是在栈上创建的。“变长”特指的代码编写阶段;编译阶段由编译器转变为定长的。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    // typedef和using都可以用于定义别名
    typedef int MYINT;
    using MYINT = int;

    P61 C++的名称空间

    1.c++ namespace的作用:定义名称空间,避免符号命名冲突。
    2.在c语言库中实现类似于c++ namespace的做法是,该c语言库中的所有外部符号都添加固定的前缀。比如GLFW库中的GFLWInit()、GLFWCreate()等。
    3.绝对不要在头文件中使用using namespace XXX。
    4.using namespace XXX的生效范围是当前作用域。尽量将using namespace XXX放在小的作用域内。

    P62 C++的线程

    #include 
    
    void DoWork() {
    	auto tid = std::this_thread::get_id(); // 获取线程id
    	std::cout <<"Thread id is " << tid << std::endl;
    	
    	using namespace std::literals::chrono_literals;	// 使用名称空间。目的是引入下文中的睡眠“1s”
    	while (1) {
    		std::cout << "Working..." << std::endl;
    		std::this_thread::sleep_for(1s); // 当前线程睡眠1秒
    	}
    }
    
    int main() {
    	std::thread worker(DoWork); // 调用thread类的构造函数,创建thread对象,并执行线程函数
    	worker.join(); // 等待线程执行结束
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    P63 C++的计时

    1.chrono库是c++标准库的一部分,不需要使用操作系统库,是平台无关的。在c++11引入。
    2.基于开始和结束时间点的计时

    #include 
    #include 
    
    int main() {
    	using namespace std::literals::chrono_literals;
    
    	auto start = std::chrono::high_resolution_clock::now();
    	std::thid_thread::sleep_for(1s);
    	auto end = std::chrono::high_resolution_clock::now();
    
    	std::chrono::durationduration = end - start;
    	std::cout << duration.count() << std::endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.对于上例的改进,基于对象生存期,自动记录耗时

    #include 
    #include 
    
    class Timer {
    	std::chrono::time_point start, end;
    	std::chrono::duration duration;
    	
    	Timer() {
    		start = std::chrono::high_resolution_clock::now();
    	}
    
    	~Timer() {
    		end = std::chrono::high_resolution_clock::now();
    		duration = end - start;
    		std::cout<< "Timer took " << duration << " s " << std::endl;
    	}
    }
    
    void TestFunc() {
    	// 对象创建时,调用构造函数自动记录开始时间点;作用域结束时,调用析构函数
    	// 自动记录结束时间点,并输出时间间隔。
    	Timer tm; 
    	
    
    	for (int i = 0; i < 100; i++) {
    		std::cout << "Hello " << i << std::endl;
    	}
    }
    
    int main() {
    	TestFunc();
    }
    
    • 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
    • 29
    • 30
    • 31
    • 32

    P68 C++的虚析构函数

    继承、多态、内存泄露与虚析构函数:

    class Base {
    	private:
    		int* m_child;
    	public:
    		Base() {m_child = new int[5]; std::cout <<"Base constructed\n"};
    		~Base() {delete[] m_child; std::cout <<"Base destructed\n"};
    }
    
    class Derived: public Base {	// 类Derived是类Base的子类
    	private:
    		int* m_array;
    	public:
    		Base() {m_array = new int[5]; std::cout <<"Derived constructed\n"};
    		~Base() {delete[] m_array; std::cout <<"Derived destructed\n"};
    }
    
    int main() {
    	Base *base = new Base();	// 调用构造函数Base()
    	delete base;						// 调用析构函数~Base()
    
    	Derived *derive = new Derived();	// 调用构造函数Base()和Derived()
    	delete derive;									// 调用析构函数~Derived()和~Base()
    
    	// 多态与内存泄露
    	Base *poly = new Derived();	// 调用构造函数Base()和Derived()
    	delete poly;								// 仅调用析构函数~Base()。没有调用~Derived(),造成内存泄露
    	
    	// 虚析构函数:虚函数与析构函数的结构
    	// 虚析构函数与普通虚函数的不同:虚析构函数要求虚析构函数,及其子类中覆写的析构函数都要执行。而普通虚函数,仅执行覆写的函数。
    	// 在Base类的析构函数~Base()定义前,加上virtual关键字,将其声明为虚析构函数后:
    	Base *poly = new Derived();	// 调用构造函数Base()和Derived()
    	delete poly;								// 调用析构函数~Derived()和~Base()
    }
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33

    P69 C++的类型转换

    1.c风格的类型转换

    int a = 10;
    double b  = (double) a;
    
    • 1
    • 2

    2.c++的类型转换:

    • static_cast静态类型转换:double b = static_cast(a);
    • dynamic_cast动态类型转换。类型于golang中的类型检查。
    • reinterpret_cast实现了类型双关,将变量的内存转换为另一种类型,而不是将变量的值赋值给另一种类型。
    • const_cast从来添加或移除const修饰符。

    P75 C++的结构化绑定

    1.多返回值处理

    #include 
    #include 
    #include 
    
    std::tuple CreatePerson() {
    	return {"Cherno", 24};
    }
    
    int main() {
    	// 通过tuple方式,返回多个值,可读性较差
    	auto person = CreatePerson();
    	std::string& name = std::get<0>(person);
    	int age = std::get<1>(persion);
    
    	// 通过std::tie方式,返回多个值,可读性较好,但是代码行数多
    	std::string name;
    	int age;
    	std::tie(name, age) = CreatePersion();
    
    	// C++17中引入的结构化绑定
    	auto[name, age] = CreatePerson();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2.shared_ptr和unique_ptr的创建

    struct Vector2 {
    	float x, y;
    }
    
    std::shared_ptr v1 = std::make_shared();
    std::shared_ptr v1 = std::shared_ptr(new Vector2);
    std::unique_ptr v1 = std::make_unique();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    P76 C++如何处理OPTIONAL数据

    1.std::optional由C++17引入。
    2.optional主要用来处理函数的返回值,更好的处理函数执行的成功与失败

    #include 
    #include 
    #include 
    
    std::optional ReadFileAsString(const std::string& filepath) {
    	std::ifstream stream(filepath);
    	if (stream) {
    		std::string result;
    		// read from file
    		stream.close();
    		return result;
    	}
    	return {};
    }
    
    int main() {
    	std::optional data = ReadFileAsString("data.txt");
    	// 处理方式一:如果data中有数据,返回数据;否则返回默认值“file not present”
    	value = data.value_or("file not present");
    	
    	// 处理方式二:直接判断data中有没有数据
    	if (data.has_value()) {
    		// value = data.value();
    	}  else {
    		// file not present
    	}
    }
    
    • 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

    P77 C++单一变量存放多种类型的数据

    1.std::variant由C++17引入。

    #include 
    #include 
    
    int main () {
    	std::variant data;
    	data = "Cherno";  // data.index()返回0,即类型列表中的类型下标
    
    	if (auto value = std::get_if(&data)) {	// 如果data中存放的是std::string类型的数据,std::get_if返回指向其内容的指针,否则返回NULL。
    		std::string& val = *value;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    P80 如何让C++字符串更快

    #include 
    #include 
    
    void PrintName(std::string_view name) {
    	std::count << "name: " << name << std::endl;
    }
    
    int main() {
    	std::string name = "Yan Chernikov";
    	// std::string firstName = name.substr(0, 3); // 字符串造成一次内存分配
    	// std::string lastName = name.substr(4, 9); // 字符串造成又一次内存分配
    
    	std::string_view firstName(name.c_str(), 3); // 无内存分配
    	std::string_view lastName(name.c_str() + 4, 9); // 无内存分配
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    std::async ??

  • 相关阅读:
    【已解决】springboot整合swagger2文档
    Javase | 集合-上
    postgresql 格式化查询树为图片 —— pgNodeGraph 与 pg_node2graph
    license授权方案
    5.【并查集】概念、代码实现、优化(Find优化、Union优化)
    什么样的蓝牙耳机打电话好用?接电话清晰的蓝牙耳机推荐
    【AGC】远程配置如何传入自定义属性
    群狼调研(长沙学校满意度调查)开展长沙游客满意度调查
    深入理解Java内存模型JMM与volatile关键字
    Linux下修改jar包中的配置文件application.conf
  • 原文地址:https://blog.csdn.net/liufuchun111/article/details/131673976