1.复制函数

1.1 浅复制

浅复制:仅复制对象的成员变量的值,而不复制对象中的指针所指向的内容。

注:如果原始对象中包含指针,浅复制将导致多个对象共享同一内存块,从而可能引发潜在的问题。当原始对象的析构函数被调用时,如果没有适当地管理共享资源,可能会导致重复释放内存或内存泄漏等问题。

1.2 深复制

深复制:不仅复制对象的成员变量的值,还要递归地复制对象中的指针所指向的内容,创建一个全新的数据拷贝。

注:新对象与原始对象彼此独立,不共享内存块,但需要更多的计算和内存开销。

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
#include <string.h> 
#include<iostream>
using namespace std;
class String{
private:
char *data;
public:
String(const char *str)
{
data=new char[strlen(str)]+1;
strcpy(data,str);
}
// String(const String &other)//浅复制
// {
// data=other.data;
// }
String(const String &other)//深复制
{
data=new char[strlen(other.data)+1];
strcpy(data,other.data);
}
~String()
{
delete []data;
}
void printData()
{
cout<<data<<endl;
}
};
int main()
{
String Str0("Hello");
String Str1(Str0);
Str1.printData();
return 0;
}

1.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
29
30
31
32
33
34
35
36
37
38
39
40
#include<bits/stdc++.h>
using namespace std;
class MyString
{
private:
char * _data;
public:
MyString(const char* s="")
:_data(nullptr)//初始化_data成员变量为nullptr
{
if(s){
size_t n=strlen(s)+1;
_data=new char[n];
memcpy(_data,s,n);
}
cout<<"create"<<endl;
}
~MyString()
{
cout<<"delete"<<endl;
delete[] _data;
}
MyString(const MyString &other)//复制构造函数(使用构造函数委托来重用构造函数)
:MyString(other._data){
cout<<"copy constructor"<<endl;
}
};
int main()
{
MyString str("hello");
MyString str2(str);
return 0;
}

out:
create
create
copy constructor
delete
delete

1.4 移动构造函数(移动)

移动构造函数:一个对象的资源从一个对象转移到另一个对象,而无需进行深层次的复制操作。

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
#include<bits/stdc++.h>
using namespace std;
class MyString
{
private:
char * _data;
public:
MyString(const char* s="")
:_data(nullptr)//初始化_data成员变量为nullptr
{
if(s){
size_t n=strlen(s)+1;
_data=new char[n];
memcpy(_data,s,n);
}
cout<<"create"<<endl;
}
~MyString()
{
cout<<"delete"<<endl;
delete[] _data;
}
MyString(MyString&& other)//移动构造函数
{
this->_data=other._data;
other._data=nullptr;
cout<<"move constructor"<<endl;
}
};
int main()
{
MyString str("hello");
MyString str2(move(str));
return 0;
}

out:
create
move constructor
delete
delete

2.单例类

单例类:使用私有构造函数、私有赋值运算符和静态实例成员

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
#include <iostream>
#include <string>
using namespace std;

class President {
private:
President() {}; // 私有的默认构造函数,防止直接创建对象
President(const President&); // 私有的复制构造函数,阻止复制对象
const President& operator=(const President&); // 私有的赋值运算符重载,阻止赋值操作

string name; // 存储总统名字的私有成员变量

public:
static President& GetInstance() // 获取唯一的总统实例的静态成员函数
{
// 使用静态局部变量确保只有一个实例会被创建
static President onlyInstance;
return onlyInstance;
}

string GetName() // 获取总统名字的公有成员函数
{
return name;
}

void SetName(string InputName) // 设置总统名字的公有成员函数
{
name = InputName;
}
};

int main() {
President& onlyPresident = President::GetInstance(); // 获取唯一的总统实例并引用它
onlyPresident.SetName("Abraham Lincoln"); // 设置总统的名字为 "Abraham Lincoln"
// 下面的注释代码演示了禁止创建多个总统实例的情况
// President second; // 不能访问构造函数
// President* third = new President(); // 不能访问构造函数
// President fourth = onlyPresident; // 不能访问复制构造函数
// onlyPresident = President::GetInstance(); // 不能访问赋值运算符重载

cout << "The name of the President is: ";
cout << President::GetInstance().GetName() << endl; // 获取并输出总统的名字

return 0;
}

3.关键字 explicit

关键字 explicit:避免隐式转换

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
#include<iostream>
using namespace std;

// 定义一个名为 Human 的类
class Human {
private:
int age; // 私有成员变量,表示人的年龄

public:
// 显式构造函数,阻止隐式类型转换
explicit Human(int humansAge) : age(humansAge) {}

};

// 函数,接受一个 Human 类型的参数
void DoSomething(Human person) {
cout << "Human sent did something" << endl;
return;
}

int main() {
// 创建一个 Human 对象 kid,设置年龄为 10
Human kid(10);

// 创建另一个 Human 对象 anotherKid,设置年龄为 11
Human anotherKid = Human(11);

// 调用函数,将 kid 对象作为参数传递给它
DoSomething(kid); // 这是有效的,因为参数的类型是明确的

// 下面两行是注释掉的代码,因为它们尝试执行隐式类型转换,会导致编译错误
// Human anotherKid2 = 11;
// DoSomething(10);

return 0;
}

注:

隐式转换:将提供的整数作为参数发送给这个构造函数,从而创建一个Human 对象。

4.友元

使用关键字 friend声明友元类或友元函数。

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
#include <iostream>
#include <string>
using namespace std;

// 定义一个名为 Human 的类
class Human {
private:
// 允许 Utility 类成员访问私有成员
friend class Utility;

string name; // 私有成员变量,表示人的姓名
int age; // 私有成员变量,表示人的年龄

public:
// 构造函数,接受人的姓名和年龄
Human(string humansName, int humansAge) {
name = humansName;
age = humansAge;
}
};

// 定义一个名为 Utility 的类
class Utility {
public:
// 静态成员函数,用于显示人的年龄
static void DisplayAge(const Human& person) {
cout << person.age << endl;
}
};

int main() {
// 创建一个 Human 对象 firstMan,初始化姓名为 "Adam" 年龄为 25
Human firstMan("Adam", 25);

// 使用友元类 Utility 来访问 private 成员 age
cout << "Accessing private member age via friend class: ";
Utility::DisplayAge(firstMan);

return 0; // 程序正常退出
}

5.继承

调用基类中被覆盖的方法

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
#include<bits/stdc++.h>
using namespace std;
class base{
private:
public:
void print()
{
printf("base\n");
}
};
class son:public base{
private:
public:
void print()
{
printf("son\n");
}
};
int main()
{
son a;
a.print();
a.base::print();
return 0;
}

out:
son
base

6.抽象基类和纯虚函数

抽象基类:计用来作为其他派生类的基础,但不能被实例化为对象

注:抽象基类至少包含一个纯虚函数,这些纯虚函数在派生类中必须被实现。

纯虚函数:在抽象基类中声明的虚函数,但没有提供实际的函数体实现。

注:纯虚函数的声明使用 virtual 关键字,并在函数声明后加上 = 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Shape {
public:
// 纯虚函数,必须在派生类中实现
virtual double Area() const = 0;
};

// 派生类 Circle
class Circle : public Shape {
private:
double radius;

public:
Circle(double r) : radius(r) {}

// 实现了抽象基类中的纯虚函数
double Area() {
return 3.14 * radius * radius;
}
};

7.虚继承

虚继承:解决了多继承中可能出现的菱形继承问题以及由此引发的二义性问题。

菱形继承问题:菱形继承问题发生在一个派生类从两个不同的基类继承,而这两个基类都继承自同一个共同的基类。派生类会继承两份相同的数据,导致二义性和内存浪费。

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<bits/stdc++.h>
using namespace std;
class CommonBase {
public:
int data;
};

class Base1 : virtual public CommonBase {
public:
void setData(int d) {
data = d;
}
};

class Base2 : virtual public CommonBase {
public:
int getData() {
return data;
}
};

class Derived : public Base1, public Base2 {
};

int main() {
Derived obj;
obj.setData(42);
int result = obj.getData();
cout << "Data: " << result << endl;
return 0;
}

8.限定符 override

限定符 override:用于显式指示派生类中的成员函数是对基类中的虚函数进行重写(覆盖)的,主要作用是提高代码的可读性和可维护性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Base {
public:
virtual void MyFunction() {
cout << "Base::MyFunction()" << endl;
}
};

class Derived : public Base {
public:
// 使用 override 明确表示覆盖了基类的虚函数
void MyFunction() override {
cout << "Derived::MyFunction()" << endl;
}
};

int main() {
Derived obj;
Base* ptr = &obj;//一个派生类对象的地址赋给一个基类指针,可以使用该指针来访问基类中定义的成员函数和数据成员。
ptr->MyFunction(); // 输出 Derived::MyFunction()
return 0;
}

9.final关键字

修饰类:表示该类不能被其他类继承。

示例:class MyFinalClass final { /* 类定义 */ };

修饰成员函数:表示该成员函数不能在派生类中被重写。

示例:virtual void MyFunction() final;

修饰变量:数值一旦在初始化之后便不能更改。

示例:final int j = 5;