跳到主要内容

C++ 类

简单的说,C++ 的类(Class)就是一个“自定义的数据类型”

如果说 int 是存整数的,double 是存小数的,那么 Class 就是让你创造一个能存“”、“汽车”或者“学生”这种复杂概念的容器。

它是 面向对象编程 (OOP) 的核心。为了理解,我们用一个图纸 vs 房子的比喻。

核心概念:图纸 vs 实体

  • 类 (Class) = 图纸 (Blueprint)

    • 它只是一张设计图,规定了房子有几扇窗、什么颜色、怎么盖。

    • 它不占空间(在内存里不占地方,直到你用它造出对象)。

  • 对象 (Object) = 房子 (Instance)

    • 这是根据图纸造出来的真实的房子。

    • 它占空间(占用内存)。

    • 你可以用同一张图纸(类),造出无数个不同的房子(对象)。

类的组成部分

一个类通常包含两样东西:

  1. 属性 (Attributes / Member Variables):它什么?(比如:颜色、重量、名字)。

  2. 行为 (Methods / Member Functions):它能什么?(比如:跑、叫、计算)。

让我们来设计一个 “狗” (Dog) 的类。

#include <iostream>
#include <string>
using namespace std;

// 1. 定义类 (这是图纸)
class Dog {
public: // 访问权限:公开的 (任何人都能访问)

// --- 属性 (变量) ---
string name;
int age;

// --- 行为 (函数) ---
void bark() {
cout << name << " 正在汪汪叫!" << endl;
}

void introduce() {
cout << "我叫 " << name << ",今年 " << age << " 岁。" << endl;
}
}; // <--- 注意!类定义结束必须有分号 ;

int main() {
// 2. 创建对象 (根据图纸造出一条真狗)
Dog myDog;

// 3. 设置属性
myDog.name = "旺财";
myDog.age = 3;

// 4. 调用行为
myDog.introduce(); // 输出: 我叫 旺财,今年 3 岁。
myDog.bark(); // 输出: 旺财 正在汪汪叫!

// 可以造另一条狗
Dog yourDog;
yourDog.name = "小白";
yourDog.bark(); // 输出: 小白 正在汪汪叫!

return 0;
}

C++ 类的一个关键点:权限控制 (public vs private)

这是 C++ 和 Python 最大的区别之一。C++ 非常注重**“封装”**,也就是保护数据不被随意修改。

  • public (公有): 谁都可以从外部访问(比如上面的 name)。

  • private (私有): 只有类自己内部的函数能访问,外部(main 函数里)不能直接改。

为什么要有 private 想象一下,如果把 age 设为 public,别人可能会写 myDog.age = -100;。这显然是不合法的。 如果是 private,我们可以写一个函数来控制:

class Dog {
private:
int age; // 私有的,外面看不见

public:
// 提供一个公开的接口来设置年龄
void setAge(int a) {

age = a; // 合法才修改
}
};

C++ 构造函数

构造函数

构造函数的作用是:**创建对象的那一瞬间,立刻将数据通过给定的数值进行初始化数据。 构造函数的 3 个铁律

  1. 名字必须和类名完全相同

  2. 没有任何返回类型(连 void 都不写)。

  3. 不用手动调用,对象创建时自动触发

2 代码示例:如何写一个构造函数

我们修改一下刚才的 Dog 类:

#include <iostream>
#include <string>
using namespace std;

class Dog {
public:
string name;
int age;

// --- 这就是构造函数 ---
// 1. 名字叫 Dog (和类名一样)
// 2. 没有返回值
// 3. 接收参数用于初始化
Dog(string n, int a) {
name = n;
age = a;
cout << "构造函数被调用了!一只新狗诞生了!" << endl;
}

void introduce() {
cout << "我叫 " << name << ",今年 " << age << " 岁。" << endl;
}
};

int main() {
// 以前的写法:Dog myDog; (现在这样写会报错,因为我们规定了必须传参数)

// 现在的写法:在创建时直接传参
Dog myDog("旺财", 3);

myDog.introduce();

return 0;
}

运行结果:

构造函数被调用了!一只新狗诞生了!
我叫 旺财,今年 3 岁。

进阶:初始化列表 (Initialization List)

在 C++ 中,你会经常看到一种写法,在函数体 {} 里赋值,而是在函数名后面加个冒号 :。这叫 初始化列表

这是 C++ 程序员的“专业写法”,效率更高。

class Dog {
public:
string name;
int age;

// 推荐写法:初始化列表
// 意思是:用参数 n 初始化 name,用参数 a 初始化 age
Dog(string n, int a) : name(n), age(a) {
// 函数体可以是空的
}
};

一个常见的“坑”:默认构造函数

  • 当你没写任何构造函数时:C++ 会好心地送你一个看不见的“默认构造函数” Dog() {},让你能写 Dog myDog;

  • 一旦你写了任何一个构造函数(比如上面的 Dog(string n, int a)):C++ 就认为“既然你自定义了,那我就不送了”。

后果: 如果你写了带参数的构造函数,就不能再简单地写 Dog myDog; 了,除非你自己再手动补写一个不带参数的构造函数:

class Dog {
public:
// 1. 带参数的构造函数
Dog(string n, int a) : name(n), age(a) {}

// 2. 手动补上无参数的构造函数 (重载)
Dog() {
name = "流浪狗";
age = 0;
}
};

// 现在下面两种写法都合法了:
Dog d1("旺财", 3); // 调用第 1 个
Dog d2; // 调用第 2 个

C++继承(Inheritance)

一句话理解:**继承就是:让一个类“拥有”另一个类的属性和功能。**生活类比:

  • 动物(Animal)是一个大类
  • 狗(Dog)是一种动物
  • 猫(Cat)也是一种动物

那么:Dog 是 Animal 的一种在 C++ 里写成:

class Dog : public Animal

意思是:Dog 继承了 Animal。

继承示例

第一步:定义父类 Animal

#include <iostream>  
#include <string>
using namespace std;

class Animal {
public:
string name;

Animal(string n) : name(n) {
cout << "Animal 构造函数被调用" << endl;
}

void eat() {
cout << name << " 正在吃东西。" << endl;
}
};

第二步:让 Dog 继承 Animal

class Dog : public Animal {  
public:
int age;
// 注意这里
// 我们要调用父类的构造函数
Dog(string n, int a) : Animal(n), age(a) {
cout << "Dog 构造函数被调用" << endl;
}

void bark() {
cout << name << " 汪汪叫!" << endl;
}
};

第三步:测试

int main() {  
Dog d("旺财", 3);

d.eat(); // 来自父类
d.bark(); // 自己的函数

return 0;
}

运行结果

Animal 构造函数被调用  
Dog 构造函数被调用
旺财 正在吃东西。
旺财 汪汪叫!

关键理解点

1. 继承语法

class 子类 : public 父类

public 表示:父类 public 的成员,在子类里仍然是 public

2. 构造函数的调用顺序

当你创建:

Dog d("旺财", 3);

发生了什么?

  1. 先调用父类构造函数

  2. 再调用子类构造函数

顺序永远是:

父类 → 子类

3. 子类可以使用父类的成员

因为:

Dog : public Animal

所以:

d.eat();

是合法的。虽然 Dog 没写 eat(), 但它“继承”了。

继承的理解

我们画一个结构图:

Animal  
├── name
└── eat()

Dog
├── 继承 name
├── 继承 eat()
└── 新增 age
└── 新增 bark()

Dog 就像:在 Animal 的基础上“扩展”功能

C++ lambda

基础例子

假如我们要在主函数中实现两个整数相乘,代码如下

#include<iostream>

using namespace std;

int multipy(int x, int y){
return x*y;
}

int main(){
int x =3;
int y =2;
int m;
m = multipy(x,y);
cout<<"x*y= "<<m<<endl;
}

还有一种方式,

#include<iostream>

using namespace std;

int multipy(int x, int y);

int main(){
int x =3;
int y =2;
int m;
m = multipy(x,y);
cout<<"x*y= "<<m<<endl;
}

int multipy(int x, int y){
return x*y;
}

在main函数中使用multipy函数需要在外部定义。Lambda 表达式让你可以直接在代码中“原地”定义函数,特别适合用于回调、简单的逻辑封装以及配合 STL 算法(如排序、遍历)使用。

核心语法结构

Lambda 的基本样子如下:

[捕获列表] (参数列表) mutable -> 返回类型 {
函数体
};
  • [] 捕获列表 (Capture Clause): 决定了 Lambda 内部如何访问外部(Lambda 之外)的变量。

  • () 参数列表 (Parameters): 和普通函数一样,传入参数。

  • -> 返回类型 (Return Type): 一般可以省略,编译器会自动推导。

  • {} 函数体: 代码逻辑。

#include<iostream>

using namespace std;


int main(){
int x =3;
int y =2;
int m;

auto multipy = [=]()
{
//x=10;
return x*y;
};

m = multipy();

cout<<"x*y= "<<m<<endl;
}

也可以像普通的函数一样,传递参数

#include<iostream>

using namespace std;


int main(){
int x =3;
int y =2;
int m;

auto multipy = [=](int z)
{
return x*y*z;
};

m = multipy(3);

cout<<"x*y*z= "<<m<<endl;
}

可以使用引用,修改使用的值

#include<iostream>

using namespace std;


int main(){
int x =3;
int y =2;
int m;

auto multipy = [&](int z)
{
x=2;
return x*y*z;
};

m = multipy(3);

cout<<"x*y*z= "<<m<<endl;
}

也可以针对特特定的参数进行设置

#include<iostream>

using namespace std;


int main(){
int x =3;
int y =2;
int m;

auto multipy = [=,&x](int z)
{
x=2;
//y=1;
return x*y*z;
};

m = multipy(3);

cout<<"x*y*z= "<<m<<endl;
}
语法含义例子
[]不捕获任何外部变量[](){ return 0; }
[=]按值捕获所有外部变量 (只读副本)[=](){ return x + y; }
[&]按引用捕获所有外部变量 (可读写)[&](){ x++; }
[x]只按值捕获 x[x](){ return x * 2; }
[&x]只按引用捕获 x[&x](){ x = 0; }
[=, &y]默认按值捕获,但 y 按引用捕获[=, &y](){ y = x + 1; }
mutable允许在 Lambda 内修改按值捕获的副本[x]() mutable { x++; }