C++ 笔记

C++ 笔记

liohi

运算符重载

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
#include<iostream>
#include<vector>

class Vector {
private:
int m_x;
int m_y;
public:
Vector(int x,int y) {
this->m_x = x;
this->m_y = y;
}

// 重载 +
Vector operator+ (Vector& other) const{
return Vector{ m_x + other.m_x,m_y + other.m_y };
}
friend std::ostream& operator<<(std::ostream& os, const Vector& other); // 由于重载 << 函数位于Vector外,访问private 需要private
};

// 重载 <<
std::ostream& operator<<(std::ostream& os,const Vector& other) {

os << other.m_x << " " << other.m_y << std::endl;
return os;
}

int main() {
Vector v1(3, 4);
Vector v2(1, 4);
std::cout << v1 + v2;
}

结果输出:4 8

模板template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <string>
#include <iostream>

template<typename T,int size>
class Array {
private:
T array[size];
public:
int getSize() const {
return size;
}
};

int main() {
Array < std:: string, 5 > a;
std::cout << a.getSize()<<std::endl;
return 0;
}

结果输出:5

函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <vector>
#include <iostream>

void PrintValue(int value) {
std::cout << "Value: " << value << std::endl;
}

void ForEach(const std::vector<int>& values,void(*Print)(int)) {
for (int value : values)
Print(value);
}

int main() {
std::vector<int> values = { 1,3,41,1,3,6};
ForEach(values, PrintValue);
return 0;
}

结果输出:

1
2
3
4
5
6
Value:  1
Value: 3
Value: 41
Value: 1
Value: 3
Value: 6

lambda 表达式

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


void ForEach(const std::vector<int>& values,void(*Print)(int)) {
for (int value : values)
Print(value);
}

int main() {
std::vector<int> values = { 1,3,41,1,3,6};
auto it = std::find_if(values.begin(), values.end(), [](int value) {return value > 3; });
std::cout << *it << std::endl; // 输出 41

auto lambda = [](int value) {std::cout << "Value: " << value << std::endl; }; // lambda 表达式
ForEach(values, lambda);

int a = 5;
auto lambda = [=](int value) {std::cout << "Value: " << a << std::endl; }; // lambda 表达式 Capture:"="
ForEach(values, lambda); // 输出 5

return 0;
}

namespace

namespace存在原因:避免命名冲突,在不同的context中使用相同的符号

:::名称操作符

thread线程

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
#include<iostream>
#include<thread>

static int s_Finished = false;
void DoWork() {

using namespace std::literals::chrono_literals;

std::cout << "Start thread id is " << std::this_thread::get_id() << std::endl; //获取线程id
while (!s_Finished)
{
std::cout << "Working...\n";
std::this_thread::sleep_for(1s);
}


}

int main() {
std::thread worker(DoWork);

std::cin.get();
s_Finished = true;

worker.join(); // 线程加入
std::cout << "Finished..\n";
std::cout << "Start thread id is " << std::this_thread::get_id() << std::endl;


std::cin.get();
}

计时器 Chrono

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
#include<thread>
#include<chrono> // chrono 计时

int main() {
using namespace std::literals::chrono_literals;

auto start = std::chrono::high_resolution_clock::now();

std::this_thread::sleep_for(1s); // 线程休眠 1s

auto end = std::chrono::high_resolution_clock::now();

std::chrono::duration<float> duration = end - start; // 计算时间
std::cout << duration.count() << "s" << std::endl;

std::cin.get();
}

输出 :1.0026s

优化…

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
#include<iostream>
#include<thread>
#include<chrono> // chrono 计时

struct Timer
{
std::chrono::time_point<std::chrono::steady_clock>start, end;
std::chrono::duration<float>duration;
Timer() {
start = std::chrono::high_resolution_clock::now();
}
~Timer() {
end = std::chrono::high_resolution_clock::now();
duration = end - start;

float ms = duration.count() * 1000.0f;
std::cout << "Time took" << ms << "ms" << std::endl;
}
};

void Function() {
Timer timer;
for (int i = 0; i < 100; i++) {
std::cout << "hello" << std::endl;
}
}

int main() {
Function();

std::cin.get();
}

输出:

1
2
3
4
5
6
7
hello
hello
.
.
.
hello
Time took 53.99912ms

排序Sort()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
#include<algorithm>
#include<vector>
#include<functional>

int main() {
std::vector<int> values = { 4,1,2,5,3 };
std::sort(values.begin(), values.end(), [] (int a,int b) { // lambda表达式
if (a == 1)return false;
if (b == 1)return true; // 将 1 移到最后
return a < b;
});

for (int value : values)
std::cout << value<<std::endl;
}

输出

2 3 4 5 1

移动语义

移动语义相比于拷贝语义优势在于只用分配一次内存即可访问或者返回对象,减少开辟堆内存时间花销

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
58
59
60
61
62
63
64
65
66
#include<iostream>
#include<string>

class String {
public:
String() = default;
String(const char* string) {
printf("Create!\n");
m_Size = strlen(string);
m_Data = new char[m_Size];
memcpy(m_Data, string, m_Size);
}

String(const String& other) {
printf("Copyed!\n");
m_Size = other.m_Size;
m_Data = new char[m_Size];
memcpy(m_Data, other.m_Data, m_Size);
}

String(String&& other) noexcept{ // 移动构造函数
printf("Moved!\n");
m_Size = other.m_Size;
m_Data = other.m_Data;

other.m_Size = 0;
other.m_Data = nullptr;
}

~String() {
printf("Destroyed!\n");
delete m_Data;
}
void Print() {
for (uint32_t i = 0; i < m_Size; i++)
putchar(m_Data[i]);
printf("\n");
}
private:
char* m_Data;
uint32_t m_Size;

};

class Entity {
public:
Entity(const String& name)
:m_name(name) {
}
Entity(String&& name) // 接收右值参数
:m_name((String&&)name) { // 显式声明name为右值传递 或者 :m_name(std::move(name)) 将左值转为右值

}
void PrintName() {
m_name.Print();
}

private:
String m_name;
};

int main() {
Entity entity("Herrick"); // 传递右值参数
entity.PrintName();
std::cin.get();
}

std::move

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
58
59
60
61
62
63
64
#include<iostream>
#include<string>

class String {
public:
String() = default;
String(const char* string) {
printf("Create!\n");
m_Size = strlen(string);
m_Data = new char[m_Size];
memcpy(m_Data, string, m_Size);
}

String& operator=(String &&other)noexcept{ // 移动构造函数

if (this != &other) {
printf("Moved!\n");

delete m_Data;

m_Size = other.m_Size;
m_Data = other.m_Data;

other.m_Size = 0;
other.m_Data = nullptr;
}
return *this;
}

~String() {
printf("Destroyed!\n");
delete m_Data;
}
void Print() {
for (uint32_t i = 0; i < m_Size; i++)
putchar(m_Data[i]);
printf("\n");
}

private:
char* m_Data;
size_t m_Size;

};


int main() {
String apple = "Apple";
String dest;

std::cout << "Apple:";
apple.Print();
std::cout << "Dest:";
dest.Print();

dest = std::move(apple);

std::cout << "Apple:";
apple.Print();
std::cout << "Dest:";
dest.Print();

std::cin.get();
}

输出:

1
2
3
4
5
6
Create!
Apple:Apple
Dest:
Moved!
Apple:
Dest:Apple

Rule of Three

C++三法则(Rule of Three)指的是在定义一个包含动态内存分配的类时,必须重载以下三个函数:

  1. 拷贝构造函数(Copy Constructor):用于将一个已存在的对象复制到新创建的对象中。
  2. 拷贝赋值操作符(Copy Assignment Operator):用于将一个已存在的对象赋值给另一个已存在的对象。
  3. 析构函数(Destructor):用于在对象被销毁时释放动态分配的内存。

这三个函数的实现都要根据类中的成员变量进行深拷贝或移动操作,否则可能会引起内存泄漏或悬空指针等问题。

Rule of Five

C++五法则(Rule of Five)在 C++11 中增加了两个函数,即移动构造函数和移动赋值操作符:

  1. 拷贝构造函数(Copy Constructor):用于将一个已存在的对象复制到新创建的对象中。
  2. 拷贝赋值操作符(Copy Assignment Operator):用于将一个已存在的对象赋值给另一个已存在的对象。
  3. 移动构造函数(Move Constructor):用于将一个右值引用的对象移动到新创建的对象中,避免不必要的拷贝开销。
  4. 移动赋值操作符(Move Assignment Operator):用于将一个右值引用的对象赋值给另一个已存在的对象,避免不必要的拷贝开销。
  5. 析构函数(Destructor):用于在对象被销毁时释放动态分配的内存。

这五个函数的实现都要根据类中的成员变量进行深拷贝或移动操作,同时避免引起内存泄漏或悬空指针等问题。

顶层 Const 和 底层 Const

概念:

  • 顶层Const ,表示修饰的本身就是常量,指的是指针,通常 * 右边
  • 底层const,表示修饰的变量所指的量是常量,指的是所指变量,通常 * 左边

例:

1
2
3
4
5
6
int a = 10;int* const b1 = &a;        //顶层const,b1本身是一个常量
const int* b2 = &a; //底层const,b2本身可变,所指的对象是常量
const int b3 = 20; //顶层const,b3是常量不可变
const int* const b4 = &a; //前一个const为底层,后一个为顶层,b4不可变
const int& b5 = a; //用于声明引用变量,都是底层const

区分作用:

  • 执行对象拷贝时有限制,常量的底层 const 不能赋值给非常量的底层 const
  • 使用命名的强制类型转换函数 const_cast 时,只能改变运算对象的底层 const
1
const int a;int const a;const int *a;int *const a;
  • int const aconst int a均表示定义常量类型 a。
  • const int *a,其中 a 为指向 int 型变量的指针,const 在 * 左侧,表示 a 指向不可变常量。(看成const (*a),对引用加 const )
  • int *const a,依旧是指针类型,表示 a 为指向整型数据的常指针。(看成 const(a),对指针 const)
  • 标题: C++ 笔记
  • 作者: liohi
  • 创建于 : 2023-06-03 23:23:44
  • 更新于 : 2023-06-08 00:15:40
  • 链接: https://liohi.github.io/2023/06/03/C++/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
 评论