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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>

using namespace std;

// 定义类
class Student {
public:
// 无参构造函数
Student() : m_age(0), m_name("") {
cout << "Student()" << endl;
}

// 有参构造函数
Student(int age, string name) : m_age(age), m_name(name) {
cout << "Student(int, string)" << endl;
}

// 拷贝构造函数
Student(const Student& s) {
cout << "Student(const Student &)" << endl;
}

// 析构函数
~Student() {
cout << "~Student" << endl;
}

// 成员函数
void show() {
cout << "age: " << m_age << ", name: " << m_name << endl;
}

private:
// 私有成员变量
int m_age;
string m_name;
};

int main() {
// 创建类对象(会调用无参构造函数)的多种写法
Student student1;
Student student2 = Student();
Student student3();
Student student4 = Student{};
Student student5{};
Student student6 = {};

// 创建类对象(会调用有参构造函数)的多种写法
Student student7 = Student(18, "Peter");
Student student8(18, "Peter");
Student student9 = Student{18, "Peter"};
Student student10{18, "Peter"};
Student student11 = {18, "Peter"};

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>

using namespace std;

// 定义类
class Student {
public:
// 无参构造函数
Student() : m_age(0), m_name("") {
cout << "Student()" << endl;
}

// 有参构造函数
Student(int age, string name) : m_age(age), m_name(name) {
cout << "Student(int, string)" << endl;
}

// 拷贝构造函数
Student(const Student& s) {
cout << "Student(const Student &)" << endl;
}

// 析构函数
~Student() {
cout << "~Student" << endl;
}

// 成员函数
void show() {
cout << "age: " << m_age << ", name: " << m_name << endl;
}

private:
// 私有成员变量
int m_age;
string m_name;
};

int main() {
Student student1;

// 拷贝对象(会调用拷贝构造函数)的多种写法
Student student2 = student1;
Student student3(student1);
Student student4{student1};
Student student5 = (student1);
Student student6 = {student1};

return 0;
}

隐式类型转换和 explicit 关键字

explicit 关键字的概述

  • 当构造函数被声明为 explicit 时,该构造函数不会参与隐式类型转换。
  • 对于单参数的构造函数,通常都建议声明为 explicit,除非有其他特殊原因。
  • 使用 explicit 后,不支持函数参数的隐式类型转换,同时不支持拷贝初始化。
  • 使用 explicit 后,只能使用直接初始化(如 T a(x)),或者使用显式类型转换(如 static_cast<T>(x))来构造对象。
1
2
3
4
5
6
7
8
class A {
explicit A(int x) {}
};

A a1(10); // ✅ 正确写法:允许直接初始化
A a2 = 10; // ❌ 错误写法:禁止拷贝初始化,禁止隐式类型转换
A a3 = A(10); // ✅ 正确写法:允许显式构造
A a4 = static_cast<A>(10); // ✅ 正确写法:允许显式类型转换
1
2
3
4
void foo(A a) {}

foo(10); // ❌ 错误写法:禁止隐式类型转换
foo(A(10)); // ✅ 正确写法:允许显式构造

explicit 关键字的使用

  • 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
#include <iostream>

using namespace std;

// 定义类
class Student {
public:
// 无参构造函数
Student() : m_age(0), m_name("") {
cout << "Student()" << endl;
}

// 有参构造函数(单个参数)
Student(int age) : m_age(age), m_name("") {
cout << "Student(int)" << endl;
}

// 有参构造函数(两个参数)
Student(int age, string name) : m_age(age), m_name(name) {
cout << "Student(int, string)" << endl;
}

// 拷贝构造函数
Student(const Student& s) {
cout << "Student(const Student &)" << endl;
}

// 析构函数
~Student() {
cout << "~Student" << endl;
}

// 成员函数
int getAge() const {
return m_age;
}

// 成员函数
string getName() const {
return m_name;
}

// 成员函数
void show() const {
cout << "age: " << m_age << ", name: " << m_name << endl;
}

private:
// 私有成员变量
int m_age;
string m_name;
};

// 全局函数(普通函数)
void print(Student stu) {
cout << "age: " << stu.getAge() << ", name: " << stu.getName() << endl;
}

int main() {
// 存在临时对象隐式类型转换,会调用单参数构造函数,相当于 Student student1(20)
Student student1 = 20;

// 存在临时对象隐式类型转换,会调用单参数构造函数,相当于 Student student2(15)
Student student2 = (12, 13, 14, 15);

// 会执行 Student student(25) 将 25 转换成一个临时的 Student 对象,导致 print () 函数可以被调用成功
print(25);

return 0;
}
  • 使用 explicit 禁止 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>

using namespace std;

// 定义类
class Student {
public:
// 无参构造函数
Student() : m_age(0), m_name("") {
cout << "Student()" << endl;
}

// 有参构造函数(单个参数)
explicit Student(int age) : m_age(age), m_name("") {
cout << "Student(int)" << endl;
}

// 有参构造函数(两个参数)
Student(int age, string name) : m_age(age), m_name(name) {
cout << "Student(int, string)" << endl;
}

// 拷贝构造函数
Student(const Student& s) {
cout << "Student(const Student &)" << endl;
}

// 析构函数
~Student() {
cout << "~Student" << endl;
}

// 成员函数
int getAge() const {
return m_age;
}

// 成员函数
string getName() const {
return m_name;
}

// 成员函数
void show() const {
cout << "age: " << m_age << ", name: " << m_name << endl;
}

private:
// 私有成员变量
int m_age;
string m_name;
};

// 全局函数(普通函数)
void print(Student stu) {
cout << "age: " << stu.getAge() << ", name: " << stu.getName() << endl;
}

int main() {
// 错误写法(编译失败),单参数构造函数使用 explicit 声明,不允许隐式类型转换
// Student student1 = 20;

// 错误写法(编译失败),单参数构造函数使用 explicit 声明,不允许隐式类型转换
// Student student2 = {20};

// 错误写法(编译失败),单参数构造函数使用 explicit 声明,不允许隐式类型转换
// Student student3 = (12, 13, 14, 15);

// 错误写法(编译失败),单参数构造函数使用 explicit 声明,不允许隐式类型转换
// print(25);

return 0;
}

成员函数返回自身对象的引用

  • 成员函数适合返回自身对象的引用的场景

    • 返回当前对象(*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>

class Counter {
public:
Counter(int v = 0) : value(v) {}

// 返回自身对象的引用
Counter& increment() {
++value;
return *this;
}

int get() const {
return value;
}

private:
int value;
};

int main() {
Counter c;
c.increment().increment().increment(); // 链式调用
std::cout << c.get() << std::endl; // 输出 3
return 0;
}
  • 在上面的成员函数 increment() 中:
    • *this 是当前对象本身
    • 返回类型是 Counter&
    • 不会产生对象拷贝
    • 常用于链式调用

总结说明

  • 在非 const 成员函数中,this 是一个指向非 const 对象的指针常量(可类比为 Counter * const this),因此 this 指向的地址不能更改,但可以通过 this 修改对象的状态。
  • const 成员函数中,this 是一个指向 const 对象的指针常量(可类比为 const Counter * const this),因此 this 指向的地址不能更改,且不可以通过 this 修改对象的状态。
  • 成员函数中返回 *this 的引用,是 C++ 中实现链式调用和高效操作的常用技巧,前提是当前对象的生命周期由调用方保证且在使用期间有效。
  • this 指针只存在于非静态成员函数中,全局函数和静态成员函数(static 修饰)不依附于具体的对象实例,因此都不能使用 this 指针。