当前位置: 首页 > news >正文

C++学习笔记

  • 基础概念

  • 类和对象

  • 重载

  • 继承

  • 多态

  • 文件操作

  • 模板

  • STL


基础概念

this 指针

  • this是一个隐含的指针,指向当前调用成员函数的对象本身(当前对象的地址)

  • *this是对指针做解引用操作,拿到指针指向的对象本身(当前对象实体)

cpp

Number& operator++() { num++; return *this; // 返回当前对象本身(而非副本) }

new 运算符

  • new是在堆区开辟空间的关键字

数组名作为参数

在C++中当数组名作为参数传递时,会自动退化为指向数组首元素的指针:

cpp

void myPrint(int a[]) // 等价于 void myPrint(int* a) void myPrint(int* a) // 两种写法等价

引用传递

cpp

void myPrint(vector<int> &v) // 传入引用可以提升效率 myPrint(v1); // 调用时直接传入变量名,编译器自动传递引用

随机数生成

cpp

int score = rand() % 40 + 60; // 生成60-99之间的随机数 // 任何数对40取余为0-39,加上60就是60-99

&符号的判断

  • 起别名:&紧跟在类型名后面,修饰变量、参数

    cpp

    int &b = a; // b是a的引用(别名)
  • 取地址:&出现在执行语句、函数调用、表达式中,且紧跟变量、对象名之前

    cpp

    int *p = &a; // 取变量a的地址 Person *p = &mp; // 取对象mp的地址

类和对象

初始化列表

cpp

class Person { public: // 相当于有参构造 Person(int a, int b, int c) : m_A(a), m_B(b), m_C(c) {} int m_A; int m_B; int m_C; };

类对象作为成员

当其他类对象作为本类成员时,构建本类会先构建其他类对象

访问权限

  • public:类内外都可以访问

  • protected:类内可以访问,类外不可以,儿子可以访问父亲中的保护内容

  • private:类内可以访问,类外不可以,儿子不可以访问父亲中的私有内容

注意

  • class默认权限是private

  • struct创建类默认权限是public

构造函数和析构函数

构造函数
  • 没有返回值

  • 在创建对象时自动调用

  • 按参数可分为:无参构造、有参构造

  • 按类型可分为:普通构造、拷贝构造

拷贝构造函数

cpp

// 格式:使用const,同时传入类对象的引用 Person(const Person &p) { age = p.age; }
析构函数
  • 将堆区开辟的数据释放

  • 没有返回值,不写void

  • 函数名与类名相同,函数前写~

  • 析构函数不可以有参数,不可以重载

  • 对象在销毁前会自动调用析构函数,只调用一次

  • 用于在对象的生命周期结束时自动执行清理工作,避免内存泄漏

构造和析构的规则
  • 构造和析构都是必须有的实现,如果没有,编译器会自动提供空的实现

  • 如果我们自己写了有参构造,编译器不提供空构造

  • 如果我们写了拷贝构造,编译器不再提供其他构造

静态成员

静态成员变量
  1. 在编译阶段分配内存

  2. 所有对象公共使用,每个对象都可以修改

  3. 在类内声明,类外初始化

cpp

class Person { public: static int a; // 类内声明 }; // 类外初始化,::是作用域解析运算符,说明Person作用域下的a int Person::a = 100;

作用域解析运算符:::告诉编译器去哪个作用域找对应的标识符,避免命名冲突

静态成员函数
  • 所有对象都可以访问

  • 只能访问静态成员变量

空对象内存

C++编译器会给每个空对象分配一个字节的内存空间,用于区分空对象占内存的位置,每个空对象也有一个独一无二的内存地址。

const修饰成员函数

  • 常函数内不可以修改成员属性

  • 成员属性声明时加关键字mutable,在常函数中可以修改

cpp

class Person { public: void func() const { // 常函数 // m_A = 100; // 错误,不能修改 m_B = 100; // 正确,mutable修饰的可以修改 } int m_A; mutable int m_B; // 常函数中可以修改 };

友元

作用:帮助函数访问成员中的私有参数

cpp

class Building { // 声明友元函数 friend void visit(Building &build); private: string m_BedRoom; }; // 友元函数定义,可以访问类中的私有成员 void visit(Building &build) { cout << build.m_BedRoom << endl; // 可以访问私有成员 }

重载

+号重载

cpp

class Person { public: // 成员函数重载 Person operator+(Person &p) { Person temp; temp.m_A = this->m_A + p.m_A; return temp; } int m_A; int m_B; }; // 全局函数重载 Person operator+(Person &p1, Person &p2) { Person temp; temp.m_A = p1.m_A + p2.m_A; return temp; }

左移运算符重载

只能利用全局函数重载,使用成员函数重载无法实现cout在左侧

cpp

ostream& operator<<(ostream& cout, Person& p) { cout << "m_A=" << p.m_A << " m_B=" << p.m_B; return cout; // 返回cout可以支持链式编程:cout << p << endl; }

递增运算符重载

cpp

class Number { public: // 前置++重载 Number& operator++() { num++; return *this; // 返回当前对象本身 } // 后置++重载,int表示占位符,用于区分前置和后置 Number operator++(int) { Number temp = *this; // 记录当前值 num++; return temp; // 返回旧值(不能返回引用) } int num; };

其他运算符重载

  • 赋值运算符重载=

  • 关系运算符重载==!=><

  • 函数调用运算符重载()


继承

继承方式

继承方式父类public父类protected父类private
公共继承publicprotected不可访问
保护继承protectedprotected不可访问
私有继承privateprivate不可访问

继承特点

  • 子类继承时,父类中所有非静态的成员属性都会被子类继承

  • 父类中私有成员属性,是被编译器隐藏了,不可访问但被子类继承了

同名成员处理

cpp

// 默认调用子类的 cout << son.m_A << endl; son.func(); // 调用父类的要加作用域 cout << son.Base::m_A << endl; son.Base::func();

菱形继承问题

利用虚继承解决:

cpp

class Animal {}; class Sheep : virtual public Animal {}; // 虚继承 class Camel : virtual public Animal {}; // 虚继承 class SheepCamel : public Sheep, public Camel {}; // 调用时不需要判断继承自哪个父类 SheepCamel sc; // sc.Animal::m_Age // 不需要这样区分

菱形继承的主要问题:子类继承两份相同的数据,导致资源浪费


多态

动态多态

cpp

class Animal { public: // 虚函数,实现动态多态 virtual void speak() { cout << "Animal speak" << endl; } }; class Dog : public Animal { public: void speak() { // 重写父类的虚函数 cout << "Dog speak" << endl; } }; // 父类的引用指向子类对象 void doSpeak(Animal &a) { a.speak(); // 根据传入对象不同调用不同函数 } void test() { Dog d; doSpeak(d); // 输出 "Dog speak" }

多态案例

cpp

class AbstractCalculator { public: virtual int getResult() { // 虚函数 return 0; } int num1; int num2; }; class AddCalculator : public AbstractCalculator { public: int getResult() { return num1 + num2; } }; // 多态的使用需要父类的指针或者引用指向子类对象 void test() { AbstractCalculator *c = new AddCalculator(); c->num1 = 10; c->num2 = 20; cout << c->getResult() << endl; // 30 delete c; // 使用完销毁 }

纯虚函数和抽象类

cpp

class Base { public: virtual void func() = 0; // 纯虚函数 }; // 只要有一个纯虚函数,这个类就是抽象类 // 抽象类特点: // 1. 无法实例化对象 // 2. 抽象类的子类必须要重写父类中的纯虚函数,否则也是抽象类 class Son : public Base { public: virtual void func() { // 重写纯虚函数 cout << "func" << endl; } }; // 这样就可以创建子类实例化对象

文件操作

C++中对文件操作需要包含头文件<fstream>

文件类型

  1. 文本文件:文件以ASCII存储

  2. 二进制文件:文件以二进制存储

写文件

cpp

#include <fstream> void test() { // 1. 创建流对象 ofstream ofs; // 2. 打开文件 ofs.open("test.txt", ios::out); // 3. 写数据 ofs << "姓名:张三" << endl; ofs << "年龄:18" << endl; // 4. 关闭文件 ofs.close(); }

常见打开方式

  • ios::in:为读文件打开文件

  • ios::out:为写文件打开文件

  • ios::ate:初始位置,文件尾

  • ios::app:追加方式写文件

  • ios::trunc:如果文件存在先删除再创建

  • ios::binary:二进制方式

文件打开方式可以配合使用,利用|操作符:

cpp

ofs.open("test.txt", ios::out | ios::binary);

读文件

cpp

#include <fstream> void test() { ifstream ifs; ifs.open("test.txt", ios::in); if (!ifs.is_open()) { // 判断文件是否打开成功 cout << "文件打开失败" << endl; return; } // 读数据 char buf[1024] = {0}; while (ifs >> buf) { cout << buf << endl; } ifs.close(); }

模板

函数模板

cpp

template <typename T> // 或者 template <class T> void mySwap(T &a, T &b) { T temp = a; a = b; b = temp; }

注意事项

  1. 自动类型推导,必须要一致的数据类型T才可以使用模板

  2. 模板必须明确T的类型,才可以使用

  3. 普通函数调用时可以发生自动类型转化(隐式转化)

  4. 函数模板调用时,如果利用自动推导,不会发生隐式转换

  5. 如果利用显示指定类型的方式,可以发生隐式转换:add<int>(a, c)

普通函数与函数模板的调用规则

  • 如果函数和模板都可以调用,优先调用普通函数

  • 可以通过空参数列表强制调用函数模板:myPrint<>(a, b)

  • 函数模板可以重载

  • 如果函数模板可以产生更好的匹配,优先调用函数模板

具体化模板

cpp

// 普通模板 template <class T> bool myCompare(T &a, T &b) { return a == b; } // 针对Person类型的具体化模板 template<> bool myCompare(Person &p1, Person &p2) { return p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age; }

类模板

cpp

template <class NameType, class AgeType> class Person { public: Person(NameType name, AgeType age) { this->m_Name = name; this->m_Age = age; } void showPerson() { cout << "name = " << m_Name << " age = " << m_Age << endl; } NameType m_Name; AgeType m_Age; }; void test() { Person<string, int> p("Tom", 10); // 必须指定类型 p.showPerson(); }

类模板特点

  • 类模板没有自动推导的使用方式,必须指定类型

  • 类模板成员函数是在调用时才创建

类模板与继承

cpp

template<class T> class Base { T m; }; // 必须指定继承的父类的参数类型,否则无法分配内存空间 class Son : public Base<int> {}; // 灵活指定父类中的模板参数类型 template<class T1, class T2> class Son2 : public Base<T2> { T1 obj; }; void test() { // 子类有string类型的成员变量obj,同时有Base<int>类型的成员变量m Son2<string, int> s; }

模板类的外部实现

cpp

template<class T1, class T2> class Person { public: Person(T1 name, T2 age); void showPerson(); T1 m_Name; T2 m_Age; }; // 类外实现构造函数 template<class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } // 类外实现成员函数 template<class T1, class T2> void Person<T1, T2>::showPerson() { cout << "name = " << this->m_Name << " age = " << this->m_Age << endl; }

STL

仿函数

概念

仿函数(函数对象)本质是重载了()运算符的类/结构体,既拥有类的特性(可保存状态),又能像普通函数一样被调用。

定义规则
  • 必须重载()运算符

  • 如果用于STL容器、算法,operator()必须声明为const

  • 重载的operator()必须是public

  • 类可以有私有成员

cpp

// 模板版比较仿函数 template <typename T> class MyCompare { public: bool operator()(const T& v1, const T& v2) const { return v1 > v2; // 降序规则 } }; // 使用 set<int, MyCompare<int>> s;

STL六大组件

  1. 容器:存放数据(vector, deque, set, map等)

  2. 算法:常用算法(sort, find, etc.)

  3. 迭代器:扮演容器与算法的胶合剂

  4. 仿函数:行为类似函数,可作为算法的策略

  5. 适配器:修饰容器、仿函数或迭代器

  6. 空间配置器:负责空间配置管理

vector容器

vector存放内置数据类型

cpp

#include <vector> #include <algorithm> void test() { vector<int> v; v.push_back(10); v.push_back(20); // 第一种遍历方式 vector<int>::iterator itBegin = v.begin(); vector<int>::iterator itEnd = v.end(); while (itBegin != itEnd) { cout << *itBegin << " "; itBegin++; } // 第二种遍历方式 for (vector<int>::iterator it = v.begin(); it != v.end(); it++) { cout << *it << " "; } // 第三种遍历方式(STL算法) for_each(v.begin(), v.end(), [](int val){ cout << val << " "; }); }
vector存放自定义数据类型

cpp

class Person { public: Person(string name, int age) : m_Name(name), m_Age(age) {} string m_Name; int m_Age; }; // 存放对象 vector<Person> v; v.push_back(Person("Tom", 18)); for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) { cout << it->m_Name << " " << it->m_Age << endl; } // 存放对象指针 vector<Person*> v; v.push_back(new Person("Tom", 18)); for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) { cout << (*it)->m_Name << " " << (*it)->m_Age << endl; }
vector容器嵌套(二维数组)

cpp

vector<vector<int>> v; vector<int> v1(10, 1); vector<int> v2(10, 2); v.push_back(v1); v.push_back(v2); for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) { for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) { cout << *vit << " "; } cout << endl; }
vector常用操作

cpp

// 容量和大小 v.empty(); // 判断是否为空 v.capacity(); // 获取容量 v.size(); // 获取大小 v.resize(15); // 重新指定大小 v.resize(15, 100); // 重新指定大小,并用100填充新位置 // 插入和删除 v.push_back(10); // 尾插 v.pop_back(); // 尾删 v.insert(v.begin(), 100); // 插入 v.insert(v.begin(), 2, 1000); // 插入多个 v.erase(v.begin()); // 删除 v.erase(v.begin(), v.end()); // 区间删除 v.clear(); // 清空 // 数据存取 v[0]; // 下标访问 v.at(0); // at访问 v.front(); // 第一个元素 v.back(); // 最后一个元素 // 互换容器 v1.swap(v2); // 交换两个容器 v.shrink_to_fit(); // 释放冗余内存,使容量等于大小 // 预留空间 v.reserve(1000); // 减少动态扩展时的扩展次数

deque容器

双端数组,头插法效率比vector高

cpp

#include <deque> deque<int> d; d.push_back(10); // 尾插 d.push_front(20); // 头插 d.pop_back(); // 尾删 d.pop_front(); // 头删 // 排序 sort(d.begin(), d.end()); // 需要头文件 <algorithm>

stack容器

栈:先进后出

cpp

#include <stack> stack<int> s; s.push(10); // 入栈 s.top(); // 查看栈顶 s.pop(); // 出栈 s.empty(); // 判断是否为空 s.size(); // 返回元素个数

queue容器

队列:先进先出

cpp

#include <queue> queue<int> q; q.push(10); // 入队 q.pop(); // 出队 q.front(); // 访问队头 q.back(); // 访问队尾

list容器

链表:可以对任意位置插入删除,占用空间比数组大,遍历速度较慢

cpp

#include <list> list<int> l; l.push_back(10); l.push_front(20); l.pop_back(); l.pop_front(); l.remove(100); // 移除所有匹配的值 // 排序和反转 l.reverse(); // 反转 l.sort(); // 升序排序 l.sort(greater<int>()); // 降序排序

set/multiset容器

所有元素插入时自动排序,底层是二叉树

cpp

#include <set> set<int> s; s.insert(10); // 插入 s.erase(10); // 删除 s.find(10); // 查找,返回迭代器 s.count(10); // 统计个数(对于set是0或1) s.empty(); // 判断是否为空 s.size(); // 返回大小 // set和multiset的区别 // set:不可以插入重复数据 // multiset:可以插入重复数据
set容器排序

cpp

// 定义仿函数 class MyCompare { public: bool operator()(int v1, int v2) const { return v1 > v2; // 降序 } }; set<int, MyCompare> s; // 传入仿函数
自定义数据类型排序

cpp

class Person { public: Person(string name, int age) : m_Name(name), m_Age(age) {} string m_Name; int m_Age; }; class ComparePerson { public: bool operator()(const Person& p1, const Person& p2) const { return p1.m_Age > p2.m_Age; } }; set<Person, ComparePerson> s;

pair队组

cpp

// 第一种创建方式 pair<string, int> p1("Tom", 18); cout << p1.first << " " << p1.second << endl; // 第二种创建方式 pair<string, int> p2 = make_pair("Jerry", 20);

map/multimap容器

map中所有元素都是pair,第一个元素为key,第二个元素为value,所有元素根据键值自动排序

cpp

#include <map> map<int, int> m; // 插入方式 m.insert(pair<int, int>(1, 10)); m.insert(make_pair(2, 20)); m.insert(map<int, int>::value_type(3, 30)); m[4] = 40; // 不建议用于插入,可用于访问 // 查找 map<int, int>::iterator pos = m.find(3); if (pos != m.end()) { cout << pos->first << " " << pos->second << endl; } // 统计 int num = m.count(3); // 对于map是0或1 // 排序 class MyCompare { public: bool operator()(int v1, int v2) const { return v1 > v2; } }; map<int, int, MyCompare> m2;

常用算法

遍历算法

cpp

#include <algorithm> // for_each for_each(v.begin(), v.end(), printFunc); // 普通函数 for_each(v.begin(), v.end(), Print()); // 仿函数 // transform transform(v1.begin(), v1.end(), v2.begin(), Func());
查找算法

cpp

find(v.begin(), v.end(), val); // 查找元素 find_if(v.begin(), v.end(), pred); // 按条件查找 adjacent_find(v.begin(), v.end()); // 查找相邻重复元素 binary_search(v.begin(), v.end(), val); // 二分查找(必须有序) count(v.begin(), v.end(), val); // 统计元素个数 count_if(v.begin(), v.end(), pred); // 按条件统计
排序算法

cpp

sort(v.begin(), v.end()); // 默认升序 sort(v.begin(), v.end(), greater<int>()); // 降序 random_shuffle(v.begin(), v.end()); // 洗牌 merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin()); // 合并 reverse(v.begin(), v.end()); // 反转
拷贝和替换算法

cpp

copy(v1.begin(), v1.end(), v2.begin()); // 拷贝 replace(v.begin(), v.end(), oldVal, newVal); // 替换 replace_if(v.begin(), v.end(), pred, newVal); // 条件替换 swap(v1, v2); // 交换
算术生成算法

cpp

#include <numeric> int total = accumulate(v.begin(), v.end(), 0); // 累加 fill(v.begin(), v.end(), val); // 填充
集合算法

cpp

// 交集(需要有序) vector<int> vTarget; vTarget.resize(min(v1.size(), v2.size())); vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin()); // 并集(需要有序) vTarget.resize(v1.size() + v2.size()); itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin()); // 差集(需要有序) vTarget.resize(max(v1.size(), v2.size())); itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
http://www.jsqmd.com/news/482832/

相关文章:

  • 外卖平台AI智能问答客服系统架构设计与实战优化
  • 老旧设备焕新:OpenCore Legacy Patcher的逆袭升级方案
  • 【2026年最新600套毕设项目分享】springboot基层智能化人员调度系统(14154)
  • PPT生成工具大揭秘!谁才是你的效率神器?
  • OpenClaw 接入飞书完整教程10分钟搭建专属 AI 助手
  • 立创·实战派ESP32-S3开发板全套资料(原理图/固件/例程)百度网盘下载中心
  • 3个技巧让AMD显卡实现Blender性能优化
  • 码农生存指南:从996到财务自由
  • 基于Web的留守儿童爱心网站的设计与实现
  • 立创ESP32-S3小智AI开发板:从开源复刻到新手友好的硬件设计优化之路
  • Vue智能客服中3D人物渲染的性能优化实战
  • genshin-wish-export:解决游戏数据管理难题的开源数据管理工具
  • 机器学习周报三十六
  • Phi-4-reasoning-vision-15B部署案例:curl health返回200但Web页面空白的CSS资源加载排查
  • 基于大语言模型的毕设实战:AI辅助开发全流程避坑指南
  • 精准掌控:MouseTester开源鼠标性能分析工具全解析
  • 手把手教你解决Moxa UPort1150在Linux下的驱动加载失败问题
  • 避开Keil5软件仿真的那些坑:STM32芯片兼容性与调试技巧
  • 解决方案:4个步骤实现智能高效的抖音直播自动录制系统
  • RMBG-2.0效果实测:复杂背景中人物发丝分割精度达99.2%(CEILab测试集)
  • windows7操作知识点详解
  • 【Android】Android 车机 + AI Agent 有没有搞头?
  • 大彩串口屏控件交互实战:如何用Lua脚本精准捕获按钮、文本和菜单事件
  • B 端拓客核验难题:精准度与成本,到底该怎么平衡?今天给大家介绍一下氪迹科技法人股东号码核验提效工具
  • SQL漏洞注入——sqlmap基础指令教学
  • Phi-3-vision-128k-instruct部署教程:vLLM服务健康检查与Chainlit联调
  • 在命令行中编译cpp文件
  • CAN总线节能秘籍:用TJA1145实现智能部分网络(Partial Networking)配置
  • 【毕设】基于STM32F103C8T6与MAX30102的心率血氧手表设计与实现
  • 使用DAMOYOLO-S与AI Agent构建自动化内容审核系统