命名空间

std 是C++的标准命名空间,它是一个定义在 C++ 标准库中的所有类、函数和变量的命名空间。使用方法如下:

1
using namespace std;
1
std::cout << "Hello" << std::endl;

自定义命名空间:

1
2
3
4
5
6
7
namespace cir {
constexpr double PI = 3.145926;
/*获取周长*/
double get_L(double R);
/*获取面积*/
double get_S(double R);
}

其中在 C++ 中,如果你想在命名空间内定义一个常量(如 PI),通常需要使用 const 关键字或者将其定义为 constexpr(C++11 及以上版本)。直接使用 double PI = 3.14; 在命名空间中是不合法的,因为命名空间不支持直接初始化变量。你需要使用 constconstexpr

接下载需要具体化函数内容有两种写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "cir.h"
/*写法1*/
double cir::get_L(double R)
{
return R*2*cir::PI;
}
double cir::get_S(double R)
{
return R*R*cir::PI;
}
/*写法2*/
namespace cir {
double get_L(double R)
{
return R*2*cir::PI;
}
double get_S(double R)
{
return R*R*cir::PI;
}
}

std包含的内容

std命名空间包含了许多类、函数和对象,例如:

  • 输入输出库(如std::cout , std::cin , std::endl

  • 容器类(如std::vector , std::map , std::set

  • 字符串类(std::string

  • 异常类(std::exception和相关子类)

  • 算法(如std::sort , std::find

  • 实用工具(如std::pair , std::tuple

  • 其他许多功能

使用建议

对于小型代码或示例代码,使用using namespace std; 通常是安全的。对于大型项目或库,建议显式地使用std:: 前缀,以避免潜在的名称冲突,并提高代码的可读性和可维护性。std命名空间是 C++ 编程的基础部分,理解和正确使用它对于编写健壮和高效的 C++ 代码至关重要。

输入输出

C++ 中的输入和输出(I/O)主要是通过标准库中的输入输出流来实现的。最常用的是iostream库,它

提供了用于输入和输出的基本流类,包括cincoutcerrclog

基本使用输出流如下:

1
std::cout << "Hello" << "woeld" << i << std::endl;

输入(箭头指向数据的方向):

1
std::cin >> i;

标准错误流 (cerr) 和标准日志流 ( clog)

cerr用于输出错误消息。与cout不同,cerr不是缓冲的,这意味着它会立即输出。

clog类似于cerr,但它是缓冲的。它通常用于记录错误和日志信息。

变量类型

C++中可以直接使用bool类型的数据,包含true,false值。

内联函数

内联函数(Inline Function)是C++中一种特殊的函数,其定义直接在每个调用点展开。这意味着编译器会将函数调用替换为函数本身的代码,这样可以减少函数调用的开销,尤其是在小型函数中。主要作用为提升代码的可读性。关键字 inline 必须与函数定义体放在一起才能使函数成为内联。在函数声明处添加 inline 关键字虽然没有错,但这种做法是无效的,编译器会忽略函数声明处的 inline 关键字。

1
2
3
inline int add(int i, int j){
return i+j;
}

Lambda匿名函数

Lambda 表达式是 C++11 引入的一种匿名函数的方式,它允许你在需要函数的地方内联地定义函数,而无需单独命名函数。相较于普通非匿名函数,匿名函数无需进行另外的声明和定义,仅当调用该匿名时,函数体才会创建,且调用完毕后会立即释放资源。因此匿名函数会更加节省空间,常用于像std::sort这样可以重写排序方式的函数。Lambda表达式基本语法如下:

1
2
3
4
5
6
//  捕获列表         参数          返回值类型
[capture clause](parameters) -> return_type{
//函数体
//可以使用捕获列表中的参数
return expression;//可选返回语句
}

Lambda 表达式由以下部分组成:

  • 捕获列表(Capture clause:用于捕获外部变量,在 Lambda 表达式中可以访问这些变量。捕获列表可以为空,也可以包含变量列表[var1, var2, ...]

  • 参数列表(Parameters):与普通函数的参数列表类似,可以为空或包含参数列表(param1,param2, ...)

  • 返回类型(Return type):Lambda表达式可以自动推断返回类型auto,也可以显式指定返回类型 -> return_type 。如果函数体只有一条返回语句,可以省略返回类型。

  • 函数体(Body):Lambda 表达式的函数体,包含需要执行的代码。

匿名函数的使用例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//例子1:
auto testReturn = [](int a1, int a2) {return a1>a2 };
bool flag = testReturn(23, 21);
cout<<flag<<endl;

//例子2:
int as = 100
auto testReturn = [](int a1, int a2) {return a1>a2 };
bool flag = testReturn(as, 21);
cout<<flag<<endl;

//例子3:
int as = 100
auto testReturn = [&](int a1, int a2) {a1 = 10return a1>a2 };
bool flag = testReturn(as, 21);
cout<<flag<<" "<<as<<endl;

重点:区分auto add = [=](int a1, int a2) {return a1+a2 };auto add = [&](int a1, int a2) {return a1+a2 }。其中的区别主要为[=][&],其中[=]表示按数值传入上方所用的变量,其中[&]表示按引用传入上方所用的变量。按数值传入的变量只可以访问不能修改,按引用传入的变量可以访问并且修改。

Lambda还有一个重要的作用就是把自身当作回调函数的参数,演示代码如下:

1
2
3
4
5
6
7
8
9
10
int get_best(int a, int b, bool (*compare)(int a, int b))
{
if(compare(a, b))return a;
else return b;
}
int main(void)
{
std::cout << get_best(12, 34, [](int a,int b)->bool{if(a<b)return true; else return false;}) << std::endl;
return 0;
}

使用该方法可以快速的编写回调函数。

类的概念是面向对象编程的核心之一,其主要目的是将数据和与数据相关的操作封装在一起。

C++ 类的基本结构通常包含:

  1. 数据成员(Attributes):定义类的属性。这些是类内部的变量,用于存储对象的状态。

  2. 成员函数(Methods):定义类的行为。这些是可以操作对象的数据成员的函数。

  3. 构造函数和析构函数:特殊的成员函数。构造函数在创建对象时自动调用,用于初始化对象。析构函数在对象销毁时调用,用于执行清理操作。

  4. 访问修饰符:如public , private , protected用于控制对类成员的访问权限。例如,public成员可以在类的外部访问,而private成员只能在类内部访问。

  5. 继承:允许一个类继承另一个类的特性。这是代码重用和多态性的关键。通过这些特性,C++ 类提供了一种强大的方式来组织和处理数据,使得代码更加模块化、易于理解和维护。

类的使用示例代码如下:

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
//定义汽车类
class car{
public:
std::string color;
std::string brand;
std::string type;
int year;

//定义函数指针,指向介绍函数
void (*PrintCarInfo)(std::string color, std::string brand, std::string type, int year);
};

void BWM(std::string color, std::string brand, std::string type, int year)
{
std::string str = "color:" + color
+ " brand:" + brand
+ " type:" + type
+ " year:" + std::to_string(year);
std::cout << str << std::endl;
}

void AOD(std::string color, std::string brand, std::string type, int year)
{
std::string str = "color:" + color
+ " brand:" + brand
+ " type:" + type
+ " year:" + std::to_string(year);
std::cout << str << std::endl;
}

int main(void)
{
/*以数值直接定义的方法*/
car a1;
a1.type = "A1";
a1.brand = "BWM";
a1.color = "RED";
a1.year = 2020;
a1.PrintCarInfo = BWM;

a1.PrintCarInfo(a1.color, a1.brand, a1.type, a1.year);

/*以指针定义的方法*/
car *Q7;
Q7 = new car();
Q7->type = "Q7";
Q7->brand = "AOD";
Q7->color = "BLACK";
Q7->year = 2018;
Q7->PrintCarInfo = AOD;

Q7->PrintCarInfo(Q7->color, Q7->brand, Q7->type, Q7->year);

return 0;
}

以上代码使用函数指针的方式传递函数,实际上还是C语言的方式。以下将展示C++类当中如何定义成员函数。

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
//定义汽车类
class car{
public:
std::string color;
std::string brand;
std::string type;
int year;

//定义函数指针,指向介绍函数但是本质上仍然为变量不是函数
void (*PrintCarInfo)(std::string color, std::string brand, std::string type, int year);

//定义真正的成员函数
void RealPrintCarInfo1()
{
std::string str = "color:" + color
+ " brand:" + brand
+ " type:" + type
+ " year:" + std::to_string(year);
std::cout << str << std::endl;
}

void RealPrintCarInfo2();//申明成员函数
};

void car::RealPrintCarInfo2()
{
std::string str = "color:" + color
+ " brand:" + brand
+ " type:" + type
+ " year:" + std::to_string(year);
std::cout << str << std::endl;
}

当然一个类可以包含一个类。在 C++中,一个类包含另一个类的对象称为组合(Composition)。这是一种常见的设计模式,用于表示一个类是由另一个类的对象组成的。这种关系通常表示一种”拥有”(”has-a”)的关系。

普通变量访问成员变量或者成员函数,使用 “.” 运算符

指针变量访问成员变量或者成员函数,使用“->”运算符,像C语言的结构体用法

示例代码如下:

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
using namespace std;
class Wheel
{
public:
string brand;
int year;
void wheelPrintInfo();
};
void Wheel::wheelPrintInfo()
{
cout << "我的轮胎品牌是:" << brand << endl;
cout << "我的轮胎日期是:" << year << endl;
}
//在 C++中,一个类包含另一个类的对象称为组合(Composition)。
class Car{ //汽车“类”
public:
//成员数据
string color; //颜色
string brand; //品牌
string type; //车型
int year; //年限
Wheel wl;
Wheel *pwl;
//其实也是成员数据,指针变量,指向函数的变量,并非真正的成员函数
void (*printCarInfo)(string color,string brand,string type, int year); //函数
指针,指向车介绍函数
void (*carRun)(string type); //函数指针,指向车运行的函数
void (*carStop)(string type); //函数指针,执行车停止的函数
void realPrintCarInfo();//声明成员函数
};
void Car::realPrintCarInfo() //在类的外部进行成员函数的实现
{
string str = "车的品牌是:" + brand
+ ",型号是: " + type
+ ",颜色是:" + color
+ ",上市年限是:" + std::to_string(year);
cout << str << endl;
}
void bwmThreePrintCarInfo(string color,string brand,string type, int year)
{
string str = "车的品牌是:" + brand
+ ",型号是: " + type
+ ",颜色是:" + color
+ ",上市年限是:" + std::to_string(year);
cout << str << endl;
}
void A6PrintCarInfo(string color,string brand,string type, int year)
{
string str = "车的品牌是:" + brand
+ ",型号是: " + type
+ ",颜色是:" + color
+ ",上市年限是:" + std::to_string(year);
cout << str << endl;
}
int main()
{
Car BWMthree;
BWMthree.color = "白色";
BWMthree.brand = "宝马";
BWMthree.type = "3系";
BWMthree.year = 2023;
BWMthree.pwl = new Wheel();
BWMthree.pwl->brand = "米其林";
BWMthree.pwl->year = 2023;
//BWMthree.wl.brand = "米其林";
//BWMthree.wl.year = 2023;
BWMthree.printCarInfo = bwmThreePrintCarInfo;
BWMthree.printCarInfo(BWMthree.color,BWMthree.brand,BWMthree.type,BWMthree.year);
BWMthree.realPrintCarInfo();
//BWMthree.wl.wheelPrintInfo();
Car *AodiA6 = new Car();
// AodiA6 = (struct Car*)malloc(sizeof(struct Car));
AodiA6->color = "黑色";
AodiA6->brand = "奥迪";
AodiA6->type = "A6";
AodiA6->year = 2008;
AodiA6->printCarInfo = A6PrintCarInfo;
AodiA6->pwl = new Wheel;
AodiA6->pwl->brand = "普利司通";
AodiA6->pwl->year = 2012;
//AodiA6->wl.brand = "马牌";
//AodiA6->wl.year = 2023;
AodiA6->printCarInfo(AodiA6->color,AodiA6->brand,AodiA6->type,AodiA6->year);
AodiA6->realPrintCarInfo();
//AodiA6->wl.wheelPrintInfo();
AodiA6->pwl->wheelPrintInfo();
return 0;
}

权限

C++中的访问权限主要分为三种:publicprivateprotected。这些权限决定了类成员(包括数

据成员和成员函数)的可访问性。以下是一个总结表格,说明了在不同情况下这些权限如何应用:

访问权限 类内部 同一个类的对象 派生类 类外部
public
private
protected

使用权限(如publicprivateprotected)在C++中是一种关键的封装手段,它们旨在控制对类成员的访问。下面是一个表格,总结了使用权限的主要好处和潜在缺点:

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
67
68
69
70
71
72
73
#include <iostream>
#include <string>
using namespace std;
/*
银行的账户是一个模板,是一个类,有存款人信息和账户额度,而具体的存款人视为一个对象,
一个对象不能私自修改账户额度,需要通过一个操作流程,比如去ATM或者柜台进行操作才能修改到账户额度,
所以,存款人信息和账户额度设计成私有权限,通过公有的操作流程,也就是公有函数去操作私有变量。
基于这个场景,我们编程实现代码
*/
class BankAccount{
private:
//有存款人信息和账户额度
string name;
string addr;
int age;
double balance;
public:
string bankAddr;
//比如去ATM或者柜台进行操作才能修改到账户额度
void registerMes(string newName, string newAddr,int newAge,double
newBalance);
void withdraw(double amount);
void deposit(double amount);
double getBalance();
void printUserInfo();
};
void BankAccount::printUserInfo()
{
string mesTem = "账户名:" + name + ",地址:" + addr +
",年龄:"+ std::to_string(age) + ",存款:" + std::to_string(balance);
cout << mesTem << endl;
}
void BankAccount::registerMes(string newName, string newAddr,int newAge,double
newBalance)
{
name = newName;
addr = newAddr;
age = newAge;
balance = newBalance;
}
// 存款方法
void BankAccount::deposit(double amount) {
if (amount > 0) {
balance += amount;
} else {
cerr << "Deposit amount must be positive." << endl;
}
}
// 取款方法
void BankAccount::withdraw(double amount) {
if (amount > balance) {
cerr << "Insufficient funds." << endl;
} else if (amount <= 0) {
cerr << "Withdrawal amount must be positive." << endl;
} else {
balance -= amount;
}
}
// 获取当前余额的方法
double BankAccount::getBalance() {
return balance;
}
int main()
{
BankAccount user1;
user1.registerMes("老陈","深圳光明区",35,100);
user1.printUserInfo();
user1.deposit(1000);
cout << user1.getBalance() << endl;
user1.withdraw(30);
cout << user1.getBalance() << endl;
return 0;
}

引用

引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。在C语言中,一个数据对应一个内存,通过由一个变量名来访问这个内存空间的数据,叫做直接访问,相对直接访问,有个间接访问的说法,叫做指针。而引用相当于又给这个内存中的数据提供了一个新的变量名,这个变量名功能比传统变量名更特殊,是直达地址的,后续代码验证!

以下代码通过使用引用变量作为参数,直接体现其直达地址的特性。

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
#include <iostream>
using namespace std;
// 函数定义
void swap(int& x, int& y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
return;
}

int main ()
{
// 局部变量声明
int a = 100;
int b = 200;
cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;
/* 调用函数来交换值 */
swap(a, b);
cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;
return 0;
}

通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。C++ 函数可以返回一个引用,方式与返回一个指针类似。当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。例如,请看下面这个简单的程序:

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
#include <iostream>
using namespace std;
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
double& setValues(int i) {
double& ref = vals[i];
return ref; // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i]
}
// 要调用上面定义函数的主函数
int main ()
{
cout << "改变前的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
setValues(1) = 20.23; // 改变第 2 个元素
setValues(3) = 70.8; // 改变第 4 个元素
cout << "改变后的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

using namespace std;

int& swap(int* point, int num)
{
int& ref = point[num];
return ref;
}

int main()
{
int x[5] = {1,2,3,4,5};

swap(x, 0) = 12;

cout << x[0] << " " << x[1] << endl;
return 0;
}

重载

函数重载在同一个作用域内,可以声明几个功能类似的同名函数,这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。下面的实例中,同名函数 print() 被用于输出不同的数据类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
class show_data{
public:
void print(int a){
cout << "int " << a << endl;
}
void print(char a){
cout << "char " << a << endl;
}
void print(char str[]){
cout << "string " << str << endl;
}
};//记得添加逗号
int main()
{
show_data p;
p.print(10086);
p.print("hello world");
p.print('a');
return 0;
}

运算符重载在C++中,运算符重载是一个允许程序员自定义各种运算符(如 + , - , == , != 等)在自定义类型(类或结构体)上的行为的特性。这意味着你可以定义类似于内置类型的运算符行为,使你的自定义类型更加直观和易于使用。

基本原则

  1. 不可以创建新的运算符:只能重载已经存在的运算符。
  2. 至少有一个操作数是用户定义的类型:不能重载两个基本类型的运算符。
  3. 不能更改运算符的优先级:重载的运算符保持其原有的优先级和结合性。
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
#include <iostream>
using namespace std;

class point{
public:
int x, y;
point operator+(point ptmp)const;
};
point point::operator+(point ptmp)const
{
point ref;
ref.x = x+ptmp.x;
ref.y = y+ptmp.y;
return ref;
}
int main()
{
point p1,p2;
p1.x = 12;
p1.y = 3;

p2.x = 34;
p2.y = 9;

point p3 = p1 + p2;
cout << p3.x << " " << p3.y <<endl;
return 0;
}

其中重载运算符需要使用operator关键字。后面添加const关键字可以保证被运算变量为常量,在计算过程中不被改变。

构造函数

类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。构造的是构造成员变量的初始化值,内存空间等构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
class car{
private:
string brand;
int year;
public:
car()
{
cout << "Class Init Success" <<endl;
brand = "BWM";
year = 2021;
cout << brand << year << endl;
}
};
int main()
{
car* p1;
p1 = new car();
return 0;
}

默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值,如下面的例子所示:

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>
using namespace std;
class car{
private:
string brand;
int year;
public:
car()
{
cout << "Class Init Success" <<endl;
brand = "BWM";
year = 2021;
cout << brand << year << endl;
}
/*使用构造函数实现同名操作,但是参数必须不同*/
car(string B, int Y)
{
cout << "Class Init Success" <<endl;
brand = B;
year = Y;
cout << brand << year << endl;
}
};
int main()
{
car* p1;
p1 = new car();
car p2("ODE", 2024);
car* p3;
return 0;
}

在C++中,使用初始化列表来初始化类的字段是一种高效的初始化方式,尤其在构造函数中。初始化列表直接在对象的构造过程中初始化成员变量,而不是先创建成员变量后再赋值。这对于提高性能尤其重要,特别是在涉及到复杂对象或引用和常量成员的情况下。初始化列表紧跟在构造函数参数列表后面,以冒号(:)开始,后跟一个或多个初始化表达式,每个表达式通常用逗号分隔。下面是使用初始化列表初始化字段的例子:

1
2
3
4
5
6
7
8
9
10
11
class MyClass {
private:
int a;
double b;
std::string c;
public:
// 使用初始化列表来初始化字段
MyClass(int x, double y, const std::string& z) : a(x), b(y), c(z) {
// 构造函数体
}
};

在这个例子中,MyClass有三个成员变量:a( int 类型)、b( double 类型)和cstd::string类型)。当创建MyClass的一个实例时,我们通过构造函数传递三个参数,这些参数被用于通过初始化列表直接初始化成员变量。初始化列表: a(x), b(y), c(z)的意思是用x初始化a,用y初始化b,用z初始化c。初始化列表的优点包括:

  1. 效率:对于非基本类型的对象,使用初始化列表比在构造函数体内赋值更高效,因为它避免了先默认构造然后再赋值的额外开销。

  2. 必要性:对于引用类型和常量类型的成员变量,必须使用初始化列表,因为这些类型的成员变量在构造函数体内不能被赋值。

  3. 顺序:成员变量的初始化顺序是按照它们在类中声明的顺序,而不是初始化列表中的顺序。使用初始化列表是C++中推荐的初始化类成员变量的方式,因为它提供了更好的性能和灵活性。

使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
class car{
private:
string brand;
int year;
public:
car(string B):brand(B),year(2021){
cout << "Class Init Success" <<endl;
cout << brand << year << endl;
}
};
int main()
{
car* p3;
p3 = new car("Li");
return 0;
}

析构函数

析构函数是C++中的一个特殊的成员函数,它在对象生命周期结束时被自动调用,用于执行对象销毁前的清理工作。析构函数特别重要,尤其是在涉及动态分配的资源(如内存、文件句柄、网络连接等)的情况下。

基本特性

  1. 名称:析构函数的名称由波浪号(~)后跟类名构成,如~MyClass()

  2. 无返回值和参数:析构函数不接受任何参数,也不返回任何值。

  3. 自动调用:当对象的生命周期结束时(例如,一个局部对象的作用域结束,或者使用delete删除一个动态分配的对象),析构函数会被自动调用。

  4. 不可重载:每个类只能有一个析构函数。

  5. 继承和多态:如果一个类是多态基类,其析构函数应该是虚的。

代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
class my_class{
private:
int* Data;
public:
my_class(int size){
Data = new int[size];
}

/*该函数为析构函数*/
~my_class(){
cout << "Function has been used" << endl;
delete[] Data;
}
};
int main()
{
my_class p1(12);
return 0;
}

关键字

this关键字

在 C++ 中,this关键字是一个指向调用对象的指针。它在成员函数内部使用,用于引用调用该函数的对象。使用this可以明确指出成员函数正在操作的是哪个对象的数据成员。下面是一个使用Car类来展示this关键字用法的示例:

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
#include <iostream>
using namespace std;
class car{
private:
string brand;
string model;
int year;
public:
car(string A, string B, int C)
{
this->brand = A;
this->model = B;
this->year = C;
}
void display();
};
void car::display()
{
cout << brand << model << year << endl;
}
int main()
{
car p4("Tsla ", "Modle Y ", 2013);
p4.display();
return 0;
}

通过this指针可以实现链式调用操作。链式调用是一种编程风格,允许对象连续调用多个方法,每个方法都返回对象自身(this指针),从而可以继续调用下一个方法。这在C++中通常用在类设计时,特别是在构建者模式(Builder Pattern)和流式API设计中。以下是一个链式调用的示例:

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
#include <iostream>
using namespace std;
class car{
private:
string brand;
string model;
int year;
public:
car()
{
cout << "Class Init Success" <<endl;
brand = "BWM";
year = 2021;
cout << brand << year << endl;
}
void display();
car& setBrand(const std::string& b) {
brand = b;
return *this;
}

// 设置型号并返回对象自身
car& setModel(const std::string& m) {
model = m;
return *this;
}

// 设置年份并返回对象自身
car& setYear(int y);
};
void car::display()
{
cout << brand << model << year << endl;
}

// 设置年份并返回对象自身
car& car::setYear(int y) {
year = y;
return *this;
}
int main()
{
//使用链式调用对对象进行操作
car p5;
p5.setBrand("NBWM ").setModel("X5 ").setYear(2010).display();

return 0;
}

new关键字

在C++中,new关键字用于动态分配内存。它是C++中处理动态内存分配的主要工具之一,允许在程序运行时根据需要分配内存。

分配单个对象:使用new可以在堆上动态分配一个对象。例如,new int会分配一个int类型的空间,并返回一个指向该空间的指针。

1
int* ptr = new int; //C语言中,int *p = (int *)malloc(sizeof(int));

分配对象数组new也可以用来分配一个对象数组。例如,new int[10]会分配一个包含10个整数的数组。

1
int* arr = new int[10]; //C语言中,int *arr = (int *)malloc(sizeof(int)*10);

delete关键字

newdelete配对使用

使用new分配的内存必须显式地通过delete(对于单个对象)或delete[](对于数组)来释放,以避免内存泄露:

释放单个对象:

1
delete ptr; // 释放 ptr 指向的对象

释放数组:

1
delete[] arr; // 释放 arr 指向的数组

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyClass {
public:
MyClass() {
std::cout << "Object created" << std::endl;
}
};
int main() {
// 分配单个对象
MyClass* myObject = new MyClass();
// 分配对象数组
int* myArray = new int[5]{1, 2, 3, 4, 5};
// 使用对象和数组...
// 释放内存
delete myObject;
delete[] myArray;
return 0;
}

在这个例子中,new被用来分配一个MyClass类型的对象和一个整数数组,然后使用deletedelete[]来释放内存。每个new都对应一个delete,保证了动态分配的内存被适当管理。

静态成员

静态成员变量

  • 定义:静态成员变量是类的所有对象共享的变量。与普通成员变量相比,无论创建了多少个类的实例,静态成员变量只有一份拷贝

  • 初始化:静态成员变量需要在类外进行初始化,通常在类的实现文件中。

  • 访问:静态成员变量可以通过类名直接访问,不需要创建类的对象。也可以通过类的对象访问。

  • 用途:常用于存储类级别的信息(例如,计数类的实例数量)或全局数据需要被类的所有实例共享。

静态成员函数

  • 定义:静态成员函数是可以不依赖于类的实例而被调用的函数。它不能访问类的非静态成员变量和非静态成员函数。

  • 访问:类似于静态成员变量,静态成员函数可以通过类名直接调用,也可以通过类的实例调用。

  • 用途:常用于实现与具体对象无关的功能,或访问静态成员变量。

使用示例如下:

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
#include <iostream>
using namespace std;
class my_class{
private:
int* Data;
static int NumOfClass;
public:
my_class(){
Data = new int[5];
NumOfClass++;
}
/*该函数为析构函数*/
~my_class(){
cout << "Function has been used" << endl;
delete[] Data;
}

static void Cout_Class()
{
NumOfClass++;
cout << NumOfClass << endl;
}
};
/*静态变量一定要在类的外面初始化*/
int my_class::NumOfClass = 0;

int main()
{
my_class p1[6];
my_class::Cout_Class();
my_class::Cout_Class();
my_class::Cout_Class();
my_class::Cout_Class();
return 0;
}

注意静态变量要在类外面初始化。静态成员变量在C++中的一个典型应用是用于跟踪类的实例数量。这个案例体现了静态成员变量的特性,它们在类的所有实例之间共享,因此适合于存储所有实例共有的信息。

继承