C++ 学习笔记(C# 开发者视角)

本人已熟练掌握C#,以下是我的C++学习笔记,自认为简洁且记录要点,发出来分享一下。

可拷贝至本地编辑器,方便折叠 。

熟悉区别后,接下来就是敲代码巩固实践啦!

欢迎大家与我交流讨论。


#include <iostream>
#include <vector>
#include <thread>
#include <stack>
#include <queue>
#include <unordered_map>
#include <map>
#include <unordered_set>
#include <set>
using namespace std;

thread_local int threadVar = 0;
void threadFunc() {
	threadVar++;
	std::cout << "ThreadVar: " << threadVar << std::endl;
}

//引用
void increment(int& x) {
	x++;
}

int main()
{
#pragma region 一.输入输出
	cout << "等待玩家输入:";
	string input;
	std::cin >> input;
	//回车键会认为没有输入
	std::cout << "等待玩家输入:" << input << endl;
	//std::      命名空间,引用后可省略
	//cout <<    命令一个命名空间中的对象去执行一个行为
	//std::endl  换行

	cout << "11111" << endl << "22222";
	//等效于下面三行代码
	cout << "11111";
	cout << std::endl;
	cout << "22222";
#pragma endregion

#pragma region	二.变量

#pragma region	1.短整数
	//	short\(signed) short (int)
	//	存储范围:-32768 ~ 32767
	//	*无符号短整数:unsigned	short (int)	存储范围:0 ~ 65535
	short s = 1;
	cout << s << endl;
	//short的其他三种写法
	signed short a = 1;
	signed short int b = 1;
	signed int e = 1;
#pragma endregion

#pragma region 2.整形
	//	int\(signed) int
	//	存储范围:-21亿多 ~ 21亿多
	//	*无符号整数:unsigned (int)	存储范围:0 ~ 42亿多 后缀u/U
	int i = 2, i2 = 3;
	signed ri = 2;
#pragma endregion

#pragma region 3.长整形
	//	long\(signed) long (int)
	//	存储范围:-21亿多 ~ 21亿多 或 -9223亿亿多 ~ 9223亿亿多
	//	可以加上后缀,明确变量类型:l/L
	//	注意:long的范围可能不同是因为在不同操作系统(Windows、UNIX / Linux等等)上
	//		它所占的存储空间不同,在windows上它往往和int的存储空间一样
	//	*无符号长整形:unsigned long (int)	后缀ul/UL
	long q = 3L;
#pragma endregion

#pragma region 4.长长整形
	//	long long\(signed) long long (int)
	//	存储范围:-9223亿亿多 ~ 9223亿亿多
	//	可以加上后缀,明确变量类型:ll/LL
	//	*无符号长长整形:unsigned long long(int)	后缀ull/ULL
	long long ll = 2000000000LL;
#pragma endregion

#pragma region 5.浮点数
	//	float
	// 	存储范围:-3.4*10^-38 ~ 3.4*10^38
	//	注意:
	//	1.申明时在小数数值后面加f/F(原因:C++中声明的小数 默认是double类型)
	//	2.存储6/7位有效数字(否则不能保证存储数值的准确性)
	float t = 12.034f;	//有效数字为5
	t = 0.004f;			//有效数字为1
	t = 1.123f;			//有效数字为4

#pragma endregion

#pragma region 6.双精度浮点数
	//	double
	// 	存储范围:-1.7*10^-308 ~ 1.7*10^308
	//	注意:C++中声明的小数 默认是double类型
	double d = 1.4;

#pragma endregion

#pragma region 7.长双精度浮点数
	//	long double
	// 	存储范围:-1.1*10^-4932 ~ 1.1*10^4932
	//	加上后缀,明确变量类型:l/L
	long double y = 1.4L;

#pragma endregion

#pragma region 8.布尔类型
	//	bool:true/false
	//	C++中 bool被当作整形对待 true:1  false:0
	bool p = true;
	p = 1;
#pragma endregion

#pragma region 9.字符类型
	//	char
	// 	存储单个字符,只用来存储ASCIII编码的字符
	//	C++中 char也能被当作整形对待 字符对应的整数就是它的ASCII码值
	char c = 33;						//ASCII码表对应“!”
	cout << "字符类型:" << c << endl;	//输出 !
	c = 'w';
	cout << "字符类型:" << c << endl;	//输出 w
#pragma endregion

#pragma region 10.字符串类型
	//	string
	// 	存储多个字符(任何字符),采用的编码规则更广泛
	string str = "c++";

#pragma endregion

#pragma region 11.枚举类型
	//	enum
	// 	用于定义一组命名的整数常量
	//	枚举值可以显式地指定整数值,
	//	例如 enum Day { Sunday = 1, Monday, Tuesday, ... };
	//	此时 Sunday 的值为 1,Monday 为 2,依此类推。
	enum Day {
		Sunday,    // 默认值为0
		Monday,    // 1
		Tuesday,   // 2
		Wednesday, // 3
		Thursday,  // 4
		Friday,    // 5
		Saturday   // 6
	};
#pragma endregion

#pragma region 12.指针类型
	//	type* / 数据类型* 指针变量名
	// 	用于表示指向类型为type的对象的指针
	int x = 10;
	int* ptr = &x;			// ptr 存储了变量 x 的内存地址
	cout << &x << endl;     // 输出 x 的地址
	cout << ptr << endl;    // 输出 ptr 的值(即 x 的地址)
	cout << *ptr << endl;	// 输出 10,因为 *ptr 访问的是 x 的值

	*ptr = 100;				// 通过指针修改 x 的值
	cout << x << endl;      // 输出修改后的 x 的值
#pragma endregion

#pragma region 13.数组类型
	//	type[] / type[size]	
	//	注意:
	//		size必须是常量
	//		数组的大小是固定的,可以通过 sizeof 运算符计算数组的大小。
	//		数组名本质上是一个指向数组第一个元素的指针。

	//如果数组未完全初始化,剩余元素会被初始化为 0
	int arr[5] = { 1, 2 };		//arr = {1, 2, 0, 0, 0}
	int* pt = arr;				// ptr 指向数组的第一个元素
	cout << *pt << endl;				// 输出 10
	cout << *(pt + 1) << endl;			// 输出 20

	int size = 5;
	//int arr[size];			// 错误:size 不是常量

	int matrix[2][3] = {		//二维数组
	{1, 2, 3},
	{4, 5, 6}
	};

	//使用 new 和 delete 运算符动态分配和释放数组
	int* array = new int[5];		// 动态分配一个包含 5 个整数的数组
	array[0] = 10;
	delete[] array;				// 释放数组内存

	int* ptrArray[5];			// 定义一个包含 5 个 int* 类型指针的数组
#pragma endregion

#pragma region 14.vector动态数组
	//	使用 std::vector 需要包含头文件 <vector>
	//	动态大小、连续存储、自动内存管理、高效访问
	//	类似于C#的list

	//定义和初始化:可存储任何类型
	std::vector<int> vec = { 1, 2, 3, 4 ,5 };// 初始化一个包含 5 个元素的 vector
	std::vector<int> vec1(10);				// 定义一个包含 10 个元素的 vector,默认值为 0
	std::vector<int> vec2(10, 42);		    // 定义一个包含 10 个元素的 vector,每个元素初始化为 42

	//访问元素:使用 [] 或 at() 访问元素
	//[] 不会检查索引是否越界。
	//at() 会检查索引是否越界,如果越界会抛出 std::out_of_range 异常。
	int x1 = vec[0];		// 访问第一个元素
	int y1 = vec.at(0);  // 访问第一个元素

	//添加和删除元素:push_back()、pop_back()
	vec.push_back(6);	// 在末尾添加元素6
	vec.pop_back();		// 删除末尾元素

	int size1 = vec.size();		// 获取元素个数
	int cap = vec.capacity();	// 获取容量

	//it的类型是 std::vector<int>::iterator() 迭代器
	//迭代器的行为类似于指针,但它是一个类对象,封装了访问容器元素的逻辑。
	auto it = vec.begin();
	it = vec.erase(it);		// 删除迭代器 it 指向的元素(删除 1,it 指向下一个元素(2))
	vec.insert(it, 100);	//在迭代器 it 指向的位置插入元素 x (在 2 前插入 100)

	//遍历元素
	std::cout << "for循环遍历vector:";
	for (int x : vec) {
		std::cout << x << " ";
	}
	std::cout << endl << "迭代器遍历vector:";
	for (auto it = vec.begin(); it != vec.end(); it++) {
		int x = 1;
		std::cout << *it << " ";
	}

	int n = 3;
	//其他常见操作
	bool isEmpty = vec.empty();	//判断是否为空
	auto begin = vec.begin();	//返回指向第一个元素的迭代器
	auto end = vec.end();		//返回指向末尾(最后一个元素之后)的迭代器
	int front = vec.front();	//返回第一个元素
	int back = vec.back();		//返回最后一个元素
	vec.clear();				//清空所有元素
	vec.resize(n);				//调整大小为 n,多出的元素用默认值填充
	vec.reserve(n);				//预留至少 n 个元素的存储空间,但不改变大小

#pragma endregion

#pragma region 15.结构体类型
	//struct 结构体名 {
	//	数据类型 成员1;
	//	数据类型 成员2;
	//	// ...
	//};

	//	结构体可以嵌套其他结构体
	//	可以定义结构体数组
	//	可以使用指针访问结构体成员
	//	struct 的成员默认是 public 的。
	struct Person {
		std::string name;
		int age;
		double height;
	};
	Person p1;  // 定义一个 Person 类型的变量 p1
	p1.name = "Alice";
	p1.age = 25;
	p1.height = 1.68;

	Person p2 = { "Bob", 30, 1.75 };
	std::cout << endl << "输出结构体成员: " << endl;
	std::cout << "Person 1: " << p1.name << ", " << p1.age << ", " << p1.height << std::endl;
	std::cout << "Person 2: " << p2.name << ", " << p2.age << ", " << p2.height << std::endl;
#pragma endregion

#pragma region 16.类类型
	//类是 C++ 中面向对象编程的核心概念,支持封装、继承和多态。
	//	类的成员可以通过访问修饰符控制访问权限。
	//	类可以包含构造函数、析构函数、成员变量和成员函数。
	class Student {
	private:
		std::string name;
		int age;

	public:
		// 构造函数
		Student(std::string n, int a) : name(n), age(a) {}
		// 析构函数
		~Student() {
			std::cout << "Object destroyed" << std::endl;
		}
		void display() {
			std::cout << "Name: " << name << ", Age: " << age << std::endl;
		}
	};
	Student student("Alice", 12);  // 定义一个 Student 类的对象 p1
	student.display();

#pragma endregion

#pragma region 17.共用体类型
	// 一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型
	// 特点:
	// 1.共享内存:
	//	  共用体的所有成员共享同一块内存空间
	//    修改一个成员的值会影响其他成员的值
	// 2.大小
	//    共用体的大小是其最大成员的大小
	// 3.同一时间只能存储其中一个成员的值
	// 4.应用场景
	//    节省内存:当需要在同一内存位置存储不同类型的数据时,可以使用共用体来节省内存。
	//	  类型转换:以不同的方式解释同一块内存
	//	  硬件编程:在嵌入式系统或硬件编程中,共用体常用于访问寄存器的不同部分。
	union Data {
		int i;
		float f;
		char c;
	};
	Data data;
	data.i = 10;	// 存储 int 值
	data.f = 3.14;  // 存储 float 值  
	// 修改一个成员的值后,其他成员的值会变得无效。
	// 修改 f 的值后,i 的值变得无效
	data.i = 1092616192;  // 0x41200000
	std::cout << "共用体类型中类型转换:" << data.f << endl;  // 输出 10.0
#pragma endregion

#pragma region 18.基于哈希表类型
	// 优点:查找、插入、删除操作效率高。
	// 缺点:无法保证元素顺序,哈希冲突时性能会下降。
	
	// 1.unordered_map
	unordered_map<string, int> hashTable;
	hashTable["apple"] = 10;
	cout << hashTable["apple"]; // 输出 10


	// 2.unordered_set
	unordered_set<int> numbers = { 1, 2, 3, 4, 5 };
	numbers.insert(6);						// 插入元素
	numbers.insert(2);					    // 不会重复插入
	if (numbers.find(3) != numbers.end()) { // 查找元素
		std::cout << "3 found in set\n";
	}
	numbers.erase(4);						// 删除元素
	for (const auto& num : numbers) {		// 遍历元素
		std::cout << num << " ";
	}
	// 大小和容量
	std::cout << "\nunordered_set大小: " << numbers.size();
	std::cout << "\nunordered_set容量: " << numbers.bucket_count();
#pragma endregion

#pragma region 19.基于红黑树类型
	// 1.映射(Map)
	// 一种有序的键值对容器,底层实现是红黑树。
	// 优点:元素有序,适合需要按顺序处理数据的场景。
	// 缺点:操作效率比 unordered_map 略低。
	map<string, int> myMap;
	myMap["apple"] = 10;
	cout << myMap["apple"]; // 输出 10

	// 2.set
	// 存储唯一元素并自动排序
	// 提供了高效的查找、插入和删除操作,时间复杂度均为 O(log n)

	set<int> numbers_set = { 5, 2, 4, 1, 3 }; // 创建set
	// 插入元素
	numbers_set.insert(6);
	numbers_set.insert(2);  // 不会重复插入
	// 查找元素
	if (numbers_set.find(3) != numbers_set.end()) {
		std::cout << "3 found in set\n";
	}
	// 删除元素
	numbers_set.erase(4);
	// 遍历元素(自动排序)
	for (const auto& num : numbers_set) {
		std::cout << num << " ";  // 输出:1 2 3 5 6
	}
	// 大小
	std::cout << "\nSize: " << numbers_set.size();

#pragma endregion

#pragma region 20.其他
	// 1.链表
	struct Node {
		int data;
		Node* next;
	};
	Node* head = nullptr;
	Node* newNode = new Node{ 10, nullptr };
	head = newNode; // 插入新节点

	// 2.栈(stack)
	// 应用场景:常用于递归、深度优先搜索等场景。
	stack<int> sta;
	sta.push(1);
	sta.push(2);
	cout << sta.top(); // 输出 2
	sta.pop();

	// 3.队列(queue)
	// 应用场景:常用于广度优先搜索、任务调度等场景。
	queue<int> que;
	que.push(1);
	que.push(2);
	cout << que.front(); // 输出 1
	que.pop();

	// 4. 双端队列(deque)
	// 双端队列允许在两端进行插入和删除操作,是栈和队列的结合体。
	// 应用场景:适合需要在两端频繁操作的场景。
	deque<int> dq;
	dq.push_back(1);
	dq.push_front(2);
	cout << dq.front(); // 输出 2
	dq.pop_front();

#pragma endregion

#pragma endregion

#pragma region 三.存储类
#pragma region 1.auto
	// 在 C++11 之前,auto 用于表示局部变量的默认存储类(现已废弃)
	// 在 C++11 及之后,auto 用于类型推导。
	// 生命周期:自动
	auto x3 = 10;       // x 的类型被推导为 int
	auto y3 = 3.14;     // y 的类型被推导为 double
#pragma endregion

#pragma region 2.register
	//提示编译器将变量存储在 CPU 寄存器中,以提高访问速度。
	//现代编译器通常会自动优化,所以C++11 开始,register 被弃用;C++17 中完全移除。
	register int counter = 0;  // 提示编译器将 counter 存储在寄存器中
#pragma endregion

#pragma region 3.static
	// 用于局部变量时,使变量的生命周期延长至整个程序运行期间。
	// 用于全局变量或函数时,限制其作用域为当前文件。
	// 生命周期:自动
	static int count = 0;  // 局部静态变量
#pragma endregion

#pragma region 4.extern
	// 用于扩展全局变量或函数的作用域,使其可以在多个文件中共享
	// 生命周期:整个程序运行期间
	// 
	// file1.cpp
	// int globalVar = 42;  // 定义全局变量
	// file2.cpp
	// extern int globalVar;  // 声明全局变量
	// std::cout << "GlobalVar: " << globalVar << std::endl;
#pragma endregion

#pragma region 5.thread_local
	// 线程局部变量在每个线程中独立存在,互不干扰。
	// 适用于多线程编程。#include <thread>
	// 生命周期:线程生命周期
	thread t1(threadFunc);
	thread t2(threadFunc);
	t1.join();
	t2.join();
#pragma endregion

#pragma region 6.mutable
	// const用于声明变量或对象为常量,即它们的值在初始化后不能被修改。
	// 当 const 用于函数参数时,它表示该参数在函数内部是只读的,不能被修改。
	// mutable 变量即使在 const 对象中也可以被修改。
	// 通常用于缓存或标记等场景。
	// 生命周期:与对象相同
	class Example {
	private:
		mutable int cache;  // mutable 变量
		int value;

	public:
		Example(int v) : value(v), cache(0) {
			// 构造函数
		}

		int getValue() const {
			cache++;  // 即使在 const 函数中也可以修改 cache
			return value;
		}
	};
	Example ex(10);
	cout << "mutable:" << ex.getValue() << endl;
#pragma endregion

#pragma region 7.引用
	// type& 引用的名称 = 被引用的变量名称;
	// 
	// 引用:一种数据类型,它允许一个变量以另一个变量的别名存在。
	//		引用传递,在功能上类似于C#的ref。
	// 特点:
	//		引用必须在声明时初始化,并且不能重新绑定到另一个对象。
	//		引用不占用额外的存储空间,直接指向被引用的对象。
	// 使用场景:
	//		当函数需要直接操作一个对象时,使用引用可以避免不必要的拷贝,提高性能。
	//		当函数需要修改传入的对象时,使用引用可以明确表达这一意图。

	int reference = 10;
	increment(reference);  // a 的值变为 11

#pragma endregion


#pragma endregion

#pragma region 四.循环
#pragma region 1.循环类型
	// 1.while
	// 2.for
	// 3.do...while
#pragma endregion
#pragma region  2.循环控制语句
	// 1.break:		终止 loop 或 switch 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句。
	// 2.continue:	跳过当次循环。
	// 3.goto:		将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。
#pragma endregion
#pragma endregion

	//return 0
	//这句代码可以省略 C++当中对于main函数会进行特殊处理
	//如何不写返回,编译器也会自动返回0
}

#pragma region 五.面向对象
#pragma region 1.继承
/*
class DerivedClass : public / protected / private BaseClass{
	// 派生类的成员
};
*/
// ———————————————————————————————————
//	继承方式		基类共有成员		基类保护成员		基类私有成员
// ———————————————————————————————————
//	公有继承			公有				保护			不可访问
//	保护继承			保护				保护			不可访问
//	私有继承			私有				私有			不可访问
// ———————————————————————————————————
//	继承:				允许派生类继承基类的属性和方法。
//	继承方式:			公有继承、保护继承和私有继承,决定了派生类如何访问基类的成员。
//	多态:				通过虚函数实现,允许派生类对象通过基类指针或引用调用不同的实现。
//	构造函数和析构函数:派生类的构造函数需要显式调用基类的构造函数。
//	虚继承:			用于解决多重继承中的菱形问题。
class Animal {
public:
	Animal() {
		cout << "Animal类构造函数" << endl;
	}
	void eat() {
		cout << "Animal类eat函数" << endl;
	}
};
class Dog : public Animal {

};
#pragma endregion

#pragma region 2.重载与重写
	//1.重载:与C#概念相同,允许使用相同的函数名定义多个函数版本,只要它们的参数列表不同即可。
	//		不能仅通过返回类型来区分重载函数。
	//		如果多个重载函数都可以匹配调用,编译器会报错。
	//		重载运算符,使得类的对象可以使用标准运算符进行操作。
	//2.重写:与C#概念相同,派生类覆盖基类虚函数的过程,用于实现多态。
	//		使用 override 关键字可以明确表示覆盖,提高代码的可读性。
	//		使用 final 关键字可以禁止进一步覆盖。
	//重写与隐藏的区别在于是否涉及虚函数以及参数列表是否一致。

class Point {
public:
	int x, y;
	// 构造函数,用于初始化点的坐标
	Point(int x, int y) : x(x), y(y) {}

	// 重载加法运算符
	Point operator+(const Point& p) {
		return Point(x + p.x, y + p.y);
	}
	// operator+ 是C++中用于重载加法运算符的特殊函数名称。
	// 它告诉编译器,当使用 + 运算符时,应该调用这个函数。
	// Point&:表示一个对 Point 类型对象的引用。
};

int main() {
	Point p1(1, 2);
	Point p2(3, 4);
	Point p3 = p1 + p2;  // 使用重载的加法运算符
	cout << "Result: (" << p3.x << ", " << p3.y << ")" << endl;
	return 0;
}

//重写
class Base {
public:
	// 虚函数:与C++虚函数相同概念
	virtual void display() {
		cout << "基类虚函数" << endl;
	}
	void display1(int x) {
		cout << "基类普通函数" << endl;
	}
	virtual void makeSound() final {  // 禁止派生类重写
		cout << "Animal makes a sound" << endl;
	}
};

class Derived : public Base {
public:
	void display() override {
		cout << "派生类重写虚函数" << endl;
	}
	void display1() {
		cout << "派生类隐藏基类的普通函数,哪怕参数列表不同" << endl;
	}
};
#pragma endregion

#pragma region 3.多态
	// C++ 主要通过 虚函数(Virtual Functions) 和 继承 实现多态。
	// 重写(override) 是 C++ 实现 运行时多态 的唯一方式(通过虚函数)。
	// 重载(overload) 属于 编译时多态,但并非经典多态的核心机制。
	// 应用场景:
	//		1.统一接口,不同实现(如 GUI 控件、游戏角色)。
	//		2.运行时动态绑定(如插件系统、回调机制)。
	//		3.工厂模式(根据输入创建不同的派生类对象)。
	
	//*纯虚函数与抽象类 ==> C#的抽象函数与抽象类

class Animal {
public:
	// 纯虚函数:没有实现的虚函数,强制派生类必须重写
	virtual void makeSound() = 0;  
};

class Dog : public Animal {
public:
	void makeSound() override {
		cout << "Dog barks: Woof!" << endl;
	}
};

Animal animal;  // 错误!Animal 是抽象类
Animal* animal = new Dog();  // 正确,可以用基类指针指向派生类对象

#pragma endregion

#pragma region 4.数据抽象
	// 通过隐藏对象的内部实现细节,仅暴露必要的接口来操作数据。
	// 将"做什么"(接口)与"怎么做"(实现)分离,从而提高代码的可维护性、安全性和复用性。
	// ---------------------------------------------------------------------------------
	// 优势						说明
	// ---------------------------------------------------------------------------------
	// 安全性			防止外部代码直接修改内部数据(如禁止负数余额)。
	// 可维护性			修改内部实现(如优化余额计算逻辑)不影响外部代码。
	// 接口稳定性		即使内部数据结构变化(如用 long 替代 double 存储金额),接口保持不变。
	// 代码复用			通过类封装通用功能(如所有银行账户共享相同的操作接口)。
	// ---------------------------------------------------------------------------------
#pragma endregion

#pragma region 5.数据封装
	// 封装:捆绑数据与行为、访问控制、隐藏实现细节
	// 封装是实现数据抽象的技术手段。
	// 如何实现数据封装?
	//		1.定义类并划分访问权限
	//		2.通过对象调用公共接口
#pragma endregion

#pragma region 6.数据接口
	// C++ 接口是使用抽象类来实现的
#pragma endregion
#pragma endregion


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏轼轼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值