大纲
C++ 面向对象
默认构造函数的使用
在 C++ 中,只要类中显式声明了任意构造函数(包括有参构造、拷贝构造、移动构造),编译器就不会再隐式(自动)生成默认构造函数,反之就会自动生成默认构造函数。如果需要默认构造函数,则必须显式声明(比如可以使用 = default)。
拷贝构造函数的自动生成
在 C++ 中,编译器除了可能自动生成默认构造函数外,还可能自动生成拷贝构造函数等特殊成员函数。
- 只要声明了任意构造函数,编译器就不会自动生成默认构造函数
1 2 3 4 5 6 7 8 9
| class A { public: A(int x) {} };
int main() { A a; A b(10); }
|
1 2 3 4
| class A { public: A(const A&) = default; };
|
- 如果想要有参构造函数,又想要无参构造函数,可以补一个默认构造函数:
1 2 3 4 5
| class A { public: A() {} A(int x) {} };
|
1 2 3 4 5
| class A { public: A() = default; A(int x) {} };
|
构造函数的多种调用方式
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 51 52
| #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; m_age = s.m_age; m_name = s.m_name; }
~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; }
|
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
| #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; m_age = s.m_age; m_name = s.m_name; }
~Student() { cout << "~Student" << endl; }
void show() { cout << "age: " << m_age << ", name: " << m_name << endl; }
private: int m_age; string m_name; };
void func1(Student s) { s.show(); }
Student func2() { Student student1(22, "David"); return student1; }
void test01() { Student student1(18, "Jim"); func1(student1); }
void test02() { Student s = func2(); }
|
总结
在 C++ 中,拷贝构造函数通常在对象以值传递方式作为函数参数,或在对象拷贝初始化且无法进行拷贝消除时被调用;而在 C++17 之后,函数返回局部对象的场景会强制进行拷贝消除,不再触发拷贝构造函数。
隐式类型转换和 explicit 关键字
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 关键字的使用
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 student2 = (12, 13, 14, 15);
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() {
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; 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 指针。