C++独有的核心特性
1. 面向对象编程(OOP)
-
类与对象:
class
、public/private
访问控制、成员函数。 -
构造函数/析构函数:对象的初始化和清理。
-
继承与多态:
virtual
函数、抽象类、override。
override关键字 和 virtual 代码示例:
// virtual(动态绑定) 虚函数声明#include <iostream> // 包含标准输入输出流库 using namespace std; // 使用标准命名空间(避免每次写std::)// 定义一个动物基类 class Animal { public:// 虚函数:允许子类重写这个函数// virtual关键字表示这个函数支持"运行时多态"virtual void speak() { cout << "动物叫\n"; // 基类默认实现} // 虚析构函数:确保通过基类指针删除子类对象时能正确调用子类析构函数virtual ~Animal() {} };// 定义Dog类,继承自Animal class Dog : public Animal { public:// override关键字(C++11):// 1. 明确表示这是重写父类的虚函数// 2. 如果父类没有这个虚函数,编译器会报错void speak() override { cout << "汪汪!\n"; // 子类特有实现}// 子类析构函数//类名前面加 ~ 是 C++ 中 析构函数 的固定语法(如 ~Dog())//当 Dog 对象被销毁时自动调用,用于 清理资源(如释放内存、关闭文件等)~Dog() {cout << "Dog对象被销毁\n"; //直接输出,无需格式 c++专属写法//标准输出流对象(位于 <iostream> 头文件中)//<<:流插入运算符,表示“输出到 cout”。} };int main() {// 创建一个Dog对象,但用Animal指针指向它// 这是面向对象编程中"父类指针指向子类对象"的典型用法Animal* pet = new Dog(); // 调用speak()函数:// 因为有virtual关键字,所以会根据实际对象类型(Dog)调用对应的函数// 这就是"运行时多态"的表现pet->speak(); // 输出"汪汪!"// 删除对象:// 因为Animal有虚析构函数,所以会先调用~Dog(),再调用~Animal()//delete pet 会先调用 Dog 的析构函数 ~Dog()(因为 Animal 的析构函数是 virtual 的)delete pet;return 0; }
抽象类:
抽象类是 C++ 中一种特殊的类,它用于定义接口和规范子类的行为。
什么是抽象类? 纯虚函数的语法:在虚函数声明后加
= 0
//纯虚函数格式 virtual 返回类型 函数名(参数) = 0;//抽象类格式 class Shape { // 抽象类 public:virtual double area() const = 0; // 纯虚函数virtual ~Shape() {} // 虚析构函数 };
定义:包含至少一个纯虚函数(Pure Virtual Function)的类。
特点:
不能直接实例化(不能创建对象)。
必须通过子类继承并实现所有纯虚函数后才能使用。
作用:
强制子类实现特定功能。
提供统一的接口规范。
抽象类的使用
子类必须实现所有纯虚函数,否则也会成为抽象类。
用途:
设计模式中的接口(如工厂模式、策略模式)。
框架设计中定义规范。
#include <iostream> using namespace std;// 抽象类:图形 class Shape { public:virtual double area() const = 0; // 纯虚函数virtual void print() const { // 普通虚函数(可选实现)cout << "这是一个图形" << endl;}virtual ~Shape() {} // 虚析构函数 };// 子类:圆形 class Circle : public Shape { private: //成员类型 成员名称; //访问修饰符,表示后续成员是私有的(只能在类内部访问)。 //冒号 : 是语法要求,表示访问权限的开始。double radius; public:Circle(double r) : radius(r) {}double area() const override { // 必须实现纯虚函数return 3.14159 * radius * radius;}void print() const override { // 重写普通虚函数cout << "这是一个圆形,面积:" << area() << endl;} };int main() {// Shape s; // 错误!不能实例化抽象类Shape* shape = new Circle(5.0); // 通过指针使用shape->print();delete shape;return 0; }//调用结果 这是一个圆形,面积:78.5397
基础代码示例
class Animal {
public:virtual void speak() = 0; // 纯虚函数(抽象类)
};
class Dog : public Animal {
public:void speak() override { cout << "Woof!"; }
};
2.引用(Reference)
-
本质是别名,比指针更安全。
基础代码示例
int a = 10;
int& ref = a; // ref是a的引用
ref = 20; // 直接修改a的值
3.函数重载(Overloading)
-
同名函数根据参数类型/数量区分。
基础代码示例
void print(int x) { /*...*/ }
void print(double x) { /*...*/ }
C++标准库(STL)
1.容器(Containers)
-
顺序容器:
vector
(动态数组)、list
(链表)、deque
(双端队列)。 -
关联容器:
map
(红黑树)、unordered_map
(哈希表)
//常见的STL算法 在 <algorithm> 中) #include <algorithm> // 对指定范围内的元素进行升序排序(默认) std::sort(起始迭代器, 结束迭代器); 调用示范: // 定义一个整型 vector 并初始化一组数字 std::vector<int> vec = {8, 6, 12, 24, 60, 125, 113};// 使用 std::sort 对 vector 中的元素进行升序排序 // vec.begin() 和 vec.end() 表示排序的范围(整个 vector) std::sort(vec.begin(), vec.end());// 使用范围 for 循环(C++11 特性)遍历排序后的 vector for (auto i : vec)// 输出当前元素到控制台,并用空格分隔std::cout << i << " ";// 最后输出一个换行符并刷新缓冲区 std::cout << std::endl; //输出 6 8 12 24 60 113 125 ===========================================// 查找第一个等于目标值的元素的迭代器 std::find(起始迭代器, 结束迭代器, 查找值); //调用示例 // 定义一个整型 vector,并初始化一组数字 std::vector<int> vec = {8, 6, 12, 24, 60, 125, 113};// 使用 std::find 查找值为 60 的元素,返回指向该元素的迭代器 auto finds = std::find(vec.begin(), vec.end(), 60);// 判断是否找到了目标值(迭代器不等于 vec.end() 表示找到) if (finds != vec.end()) {// 找到后使用 * 解引用操作符访问元素值,并输出结果std::cout << "find :" << *finds; } //输出find: 60========================================= // 将一个范围的元素复制到另一个位置 std::copy(源起始迭代器, 源结束迭代器, 目标起始迭代器); //调用示例 std::vector<int> vec = {8, 6, 12, 24, 60, 125, 113};std::vector<int> strs(vec.size()); // 创建一个相同大小的目标容器// 使用 std::copy 将 vec 的内容复制到 strs 中std::copy(vec.begin(), vec.end(), strs.begin());// 输出复制后的内容for (auto i : strs) {std::cout << i << " ";} //输出 8 6 12 24 60 125 113 =========================================== // 将一个范围的元素变换后复制到另一个位置 std::transform(源起始迭代器, 源结束迭代器, 目标起始迭代器, 变换函数); //调用示例 int must(int x) {return x*2; }int main() { std::vector<int> strs(vec.size());std::vector<int> vec={8,6,12,24,60,125,113};std::transform(vec.begin(),vec.end(),strs.begin(),must);for(auto i:strs){std::cout<<i<<" ";}return 0;} //输出 16 12 24 48 120 250 226=========================================== // 统计范围内等于目标值的元素个数 std::count(起始迭代器, 结束迭代器, 查找值); //调用示例std::vector<int> vec = {8, 6, 12, 24, 60, 125, 113};auto it= std::count(vec.begin(), vec.end(), 24);std::cout << it << " "; //输出1======================================= //将 [起始迭代器, 结束迭代器) 范围内的元素顺序反转。 std::reverse(起始迭代器, 结束迭代器); //调用示例std::vector<int> vec = {8, 6, 12, 24, 60, 125, 113};std::reverse(vec.begin(), vec.end());for(auto it : vec)std::cout << it << " "; //内部一行代码 可省略 { } //输出 113 125 60 24 12 6 8
示例代码:
#include <vector> //加载关键库
// 创建一个整型 vector 并初始化为 {1, 2, 3}
std::vector<int> vec = {1, 2, 3};// 向 vector 尾部添加一个元素 4
vec.push_back(4);// 使用范围 for 循环(range-based for loop)遍历 vector 中的每个元素
for (int nums : vec) {// 输出当前元素到控制台,并换行 std:endl用于换行std::cout << nums << std::endl;
}
//输出
1
2
3
4
2. 算法(Algorithms)
-
泛型操作:
sort
、find
、transform
。
示例代码:
std::sort(nums.begin(), nums.end()); // 排序 前面已展示调用
3.智能指针(Smart Pointers)
-
自动内存管理:
unique_ptr
、shared_ptr
。
示例代码:
#include <memory> //关键库std::shared_ptr<int> ptr = std::make_shared<int>(42);1. 创建了一个 int 类型的智能指针(shared_ptr)
std::shared_ptr<int> 是 C++ 中的一种引用计数型智能指针,用于管理一个动态分配的 int 对象。
当最后一个指向该对象的 shared_ptr 被销毁或重置时,对象会自动被删除(释放内存)。
✅ 2. 使用 std::make_shared 分配并初始化一个值为 42 的 int
std::make_shared<int>(42):
在堆上分配一个 int 类型的对象;
并将它的初始值设置为 42;
返回一个类型为 std::shared_ptr<int> 的智能指针,指向这个新分配的对象。
这是比直接使用 new 更推荐的方式,因为:更安全(避免内存泄漏);
性能更好(通常只进行一次内存分配);
语法更简洁清晰。
✅ 3. 将智能指针赋值给变量 ptr
std::shared_ptr<int> ptr = ...
现在 ptr 指向堆上的那个 int(42);
它负责在其不再被使用时(引用计数归零)自动释放这块内存。
C++与C的差异点
特性 | C | C++ |
---|---|---|
内存管理 | malloc/free | new/delete + 智能指针 |
字符串 | char[] | std::string (自动管理内存) |
错误处理 | 返回错误码 | try/catch 异常机制 |
泛型编程 | 无 | 模板(template ) |
进阶特性
1.模板(Templates)
-
泛型编程:函数模板和类模板。
代码示例:
template <typename T>
T max(T a, T b) { return a > b ? a : b; }这是一个通用的 max 函数模板,用于比较两个相同类型的值 a 和 b,并返回其中较大的那个。🔍 参数与返回值:
T a:第一个参数
T b:第二个参数
返回值类型为 T,即与输入相同的类型如果你希望支持字符串内容比较,可以重载或特化该模板:
const char* max(const char* a, const char* b) {return std::strcmp(a, b) > 0 ? a : b;
}
2.Lambda表达式
-
匿名函数,简化回调。
代码示例:
auto sum = [](int a, int b) { return a + b; };//调用示例auto a=[](int x,int y){return x+y;};std::cout << a(1, 2);
//输出3
3.移动语义(C++11)
-
std::move
、右值引用,提升性能。
代码示例:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); // v1的资源被转移//调用示例std::vector<int> v1 = {1, 2, 3};std::vector<int> v2 = std::move(v1); // v1的资源被转移for (auto i : v1){std::cout <<"v1="<< i << " "; //不会输出任何东西}for (auto i : v2){std::cout << "v2="<<i << " ";std::cout << std::endl;}
//输出
v2=1
v2=2
v2=3
特殊语法和操作符
1. 成员访问操作符:->
和 ::
->
操作符
-
在C中:仅用于结构体指针访问成员(
ptr->member
)。 -
在C++中:
-
除了用于类和结构体指针,还可以被重载(如智能指针、迭代器)。
-
用于访问对象的成员函数或数据成员。
-
代码示例:
class MyClass {
public:void print() { std::cout << "Hello"; }
};MyClass obj; //实例化对象 可用obj.print() 调用成员函数//ptr 指向了 obj 这个对象
MyClass* ptr = &obj; //定义 一个 MyClass 类型的指针 ptr 并 赋值 obj的地址 //->只能用于指针(pointer)类型,它用于通过指针访问对象的成员(包括成员变量和成员函数)。
ptr->print(); // 等价于 (*ptr).print()
::
作用域解析符
-
访问全局变量(当局部变量同名时):
代码示例:
int x = 10;
void foo() {int x = 20;std::cout << ::x; // 输出全局的x(10)
}
- 访问类的静态成员:
代码示例:
class MyClass
{
public:static int count;//count 是静态成员变量,属于整个类,而不是某个对象;MyClass() //MyClass 的默认构造函数 对象被创建时自动调用 重点:命名必须与类名相同{count++;}~MyClass()//MyClass 的析构函数 对象被销毁时自动调用 重点:命名必须与类名相同{count--;}
};
int MyClass::count = 0; //必须在类外定义 int main(){{MyClass a,b,c;//会依次调用 三次 MyClass 的构造函数;每次构造函数执行时都会运行 ++count;//所以 count 自动变为 3;//当这些对象生命周期结束时(如离开作用域),会调用三次析构函数,执行--countstd::cout << MyClass::count << std::endl;//输出3} //用{ } 限定生命周期 当离开该作用域之后std::cout << MyClass::count << std::endl;//输出0return 0;
}
知识拓展:
可以手动加 { }
来限定作用域吗? 可以,C 和 C++ 都支持
2. 初始化列表(Initializer List)
C++11引入的统一初始化语法:
// C++11之后的初始化方式
std::vector<int> nums = {1, 2, 3};// 初始化列表 大括号列表进行初始化是 C++11 引入的新特性;int arr[]{1, 2, 3}; // 数组初始化 自动推导大小的数组初始化方式。
class Point {
public:Point(int x, int y) : x_(x), y_(y) {} // 成员初始化列表//Point(int x, int y) 是构造函数,接收两个参数;// : x_(x), y_(y) 是成员初始化列表,//表示://把传进来的 x 赋值给成员变量 x_//把传进来的 y 赋值给成员变量 y_// 私有成员变量的声明部分
private: //私有成员只能被该类的成员函数或友元函数访问,外部无法直接访问。int x_, y_;
};//调用示例Point p(10, 20);std::cout << p.x_; // ❌ 编译错误:'int Point::x_' is private 私有化成员不能外部访问//正确做法是在内部声明输出函数 用于输出 成员变量的值
知识拓展:
private
是不是限制了成员只能类内使用? 是的,这是 封装的核心机制 如何外部访问 当私有成员之后 ? 可以通过在内部 封装函数来读取 和修改 例如:class Point { public:Point(int x, int y) : x_(x), y_(y) {} // 成员初始化列表//加const 表示这个函数不会修改类的任何成员变量//典型只读操作,应加 const。int getx() const {return x_;}//调用该函数就可返回x_的值void setx(int x) {if(x>0)x_=x; }//调用该函数可以给私有变量赋值 friend void printPoint(const Point& p); // 允许这个函数访问私有成员private: int x_, y_; }; //注意友函数声明在类外部 void printPoint(const Point& p) {std::cout << "x: " << p.x_ << ", y: " << p.y_; }
知识拓展:
重载(为同一个函数提供两个版本)
在
class
中,默认是private
:class myclasss{
int x,y; //默认私有
private
public:
//如果 int x,y; 在public 中就是公开的 外部可以访问和修改
}
class MyClass { private:int value; // 声明私有成员变量 public:int getValue() { //用于普通对象 MyClass areturn value; }int getValue() const { //用于常量对象 const MyClass areturn value; } };
3.范围for
循环(Range-based for loop)
C++11新增的简化遍历语法:
std::vector<int> nums = {1, 2, 3};
for (int num : nums) { // 遍历容器std::cout << num;
}
4.auto
类型推导
自动推断变量类型(C++11):
auto x = 10; // x是int
auto str = "C++"; // str是const char*
auto ptr = std::make_shared<int>(42); // ptr是std::shared_ptr<int>
5.nullptr
替代 NULL
C++11引入的空指针常量,类型安全:
int* ptr = nullptr; // 推荐用法(C++11+)
if (ptr == nullptr) { /*...*/ }
6.运算符重载(Operator Overloading)
标准重载命名格式写法
operator+(加法)operator-(减法)operator=(赋值)operator[](下标访问)operator()(函数调用)
允许自定义操作符行为:
class Vector {
public:Vector operator+(const Vector& other) { // operator+ 调用取决于是否 使用 + return Vector(x + other.x, y + other.y);}int x, y;
};
Vector v1{1, 2}, v2{3, 4};
Vector v3 = v1 + v2; // 调用重载的+//流程
v1触发成员函数调用传递 v2的值
返回的就是 Vector(1+3,2+4) // 直接调用构造函数 因为类内部包含公共变量 x,y
v3就是 v3{4,6}
7.
using
替代 typedef
(C++11)
更直观的类型别名:
using IntVector = std::vector<int>; // C++11
typedef std::vector<int> IntVector; // C风格(仍可用)//只在当前作用域有效 和 变量声明类似using std::cout;//作用是告诉编译器:“从现在开始,当我写 cout 的时候,其实我指的是 std::cout"cout<<"Hello";
8.结构化绑定(C++17)
一种从复杂数据结构(如对象、数组、元组等)中提取值并赋值给变量的技术。
解包结构体或元组:
std::pair<int, std::string> p{1, "C++"};
auto [id, name] = p; // id=1, name="C++"
//上面这条指令如果遇到编译错误 需要手动打开c++17 或者 手动编译文件
g++ test.cpp -o test -std=c++17
g++ 自动链接库文件编译
test.cpp 源代码文件名
-o 操作生成
test 生成test.exe
-std=c++17 用c++17规则编译std::pair<int,int> p{2,6}; // 拆解 2 6auto [times,num]=p;std::cout<<times<<" "<<num<<std::endl;//输出 2 6 std::pair<数据类型,数据类型> 自定义名{数据,数据};
注意事项: pair 固定只能解2个 auto [解1,解2]=自定义名
std::tuple<多个> std::tuple<int,int,std::string> p{2,6,"hello"};auto [times,num,outs]=p;std::cout<<times<<" "<<num<<outs<<std::endl;//输出2 6 hello
9.三向比较符 <=>
(C++20)
a <=> b
是一个三路比较运算符,它可以返回:
a < b
→ 返回负值(比如-1
)a == b
→ 返回0
a > b
→ 返回正值(比如1
)
注意事项: 以下代码中 cmp 返回的并不是 数字 而是 枚举类(enum class)
可以用if去对比 也可以 显式转换成 int 类型 例如
int result = (cmp > 0) ? 1 : ((cmp < 0) ? -1 : 0);
简化比较操作:
int a=10,bc=8;auto cmp=(a <=> bc);if (cmp < 0) {std::cout << "a < b";} else if (cmp == 0) {std::cout << "a == b";} else {std::cout << "a > b";}如果遇到编译问题 用以下指令编译
g++ test.cpp -o test -std=c++20
g++ 源程序.cpp -o 程序名 -std=c++20
C vs C++特殊语法
语法 | C | C++ |
---|---|---|
-> | 仅用于结构体指针 | 可重载(如智能指针、迭代器) |
:: | 无 | 访问全局变量、命名空间、类静态成员 |
初始化 | int arr[3] = {1, 2, 3}; | std::vector<int> nums{1, 2, 3}; |
auto | 无 | 自动类型推导(C++11) |
nullptr | 使用NULL (通常是0) | 类型安全的空指针(C++11) |
Lambda | 无 | 支持匿名函数(C++11) |