大纲 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 之后,函数返回局部对象的场景会强制进行拷贝消除,不再触发拷贝构造函数。
类型转换构造函数和运算符 类型转换构造函数 关键点
在 C++ 中,类型转换构造函数是一种特殊的构造函数,它的主要作用是将某个其他数据类型的对象转换成该类类型的对象。通常,它只有一个参数,该参数不是本类的 const 引用,而是待转换的数据类型。在类型转换构造函数中,需要明确指定转换的方法,以便正确完成从源类型到目标类型的转换。
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 #include <iostream> using namespace std;class MyInt {public : MyInt (int i = 0 ) : m_num (i) { cout << "MyInt(int)" << endl; } ~MyInt () { cout << "~MyInt()" << endl; } int getNumber () { return m_num; } private : int m_num; }; int main () { MyInt i = 10 ; cout << i.getNumber () << endl; MyInt i2 (30 ) ; cout << i2.getNumber () << endl; return 0 ; }
程序运行输出的结果如下:
1 2 3 4 5 6 MyInt(int) 10 MyInt(int) 30 ~MyInt() ~MyInt()
类型转换运算符 关键点
C++ 中的类型转换运算符(又称类型转换函数)是一种特殊的成员函数,其作用与类型转换构造函数相反,用于将类类型对象转换为其他数据类型。其基本语法为 operator type() const;,其中 type 表示要转换的目标类型,可以是任何有效的返回类型(包括数组指针、函数指针、引用等)。该函数不能指定返回类型,但实际返回 type 类型的值;它没有形参,因为类型转换是隐式执行的,无法传递参数;函数通常定义为类的成员函数,const 修饰符为可选项,表示转换过程中不应修改待转换对象的内容。
类型转换运算符的使用 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> using namespace std;class MyInt {public : MyInt (int i = 0 ) : m_num (i) { } ~MyInt () { } operator int () const { cout << "operator int() const" << endl; return m_num; } private : int m_num; }; int main () { MyInt i (30 ) ; int i2 = i + 10 ; cout << i2 << endl; int i3 = i.operator int () + 15 ; cout << i3 << endl; int i4 = static_cast <int >(i) + 25 ; cout << i4 << endl; return 0 ; }
程序运行输出的结果如下:
1 2 3 4 5 6 operator int() const 40 operator int() const 45 operator int() const 55
类对象转化为函数指针 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 #include <functional> #include <iostream> using namespace std;class MyInt {public : using tfpoint = void (*)(int ); static void func (int i) { cout << "func(int)" << endl; } MyInt (int i = 0 ) : m_num (i) { } ~MyInt () { } operator tfpoint () { return func; } private : int m_num; }; int main () { MyInt i (10 ) ; i (3 ); i.operator MyInt::tfpoint () (5 ) ; 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 #include <iostream> using namespace std;class MyInt {public : MyInt (int i = 0 ) : m_num (i) { } ~MyInt () { } operator int () const { cout << "operator int() const" << endl; return m_num; } operator double () const { cout << "operator int() const" << endl; return m_num; } private : int m_num; }; int main () { MyInt i (30 ) ; return 0 ; }
隐式类型转换和 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 ; }
类成员指针的使用 类成员函数指针 关键点
C++ 的类成员函数指针是一种特殊指针,用于指向类中的成员函数。对于非静态成员函数(包括虚函数)的函数指针,声明时需要使用 类名::* 指针变量名 的格式,获取函数地址时则通过 & 类名::成员函数名 来获得,这个地址是真正在内存中的函数地址。 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 #include <iostream> using namespace std;class MyTest {public : void commonFunc (int i) { cout << "commonFunc(" << i << ")" << endl; } }; int main () { void (MyTest::*ptrCommonFunc)(int ); ptrCommonFunc = &MyTest::commonFunc; MyTest t1, *ptr1; ptr1 = &t1; (t1.*ptrCommonFunc)(10 ); (ptr1->*ptrCommonFunc)(20 ); return 0 ; }
程序运行输出的结果如下:
1 2 commonFunc(10) commonFunc(20)
针对虚函数的使用
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 MyTest {public : virtual void virtualFunc (int i) { cout << "virtualFunc(" << i << ")" << endl; } }; int main () { void (MyTest::*ptrVirtualFunc)(int ); ptrVirtualFunc = &MyTest::virtualFunc; MyTest t1, *ptr1; ptr1 = &t1; (t1.*ptrVirtualFunc)(10 ); (ptr1->*ptrVirtualFunc)(20 ); return 0 ; }
程序运行输出的结果如下:
1 2 virtualFunc(10) virtualFunc(20)
针对静态成员函数的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std;class MyTest {public : static void staticFunc (int i) { cout << "staticFunc(" << i << ")" << endl; } }; int main () { void (*ptrStaticFunc)(int ); ptrStaticFunc = &MyTest::staticFunc; ptrStaticFunc (20 ); 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 #include <iostream> using namespace std;class MyTest {public : int m_common = 0 ; static int m_static; }; int MyTest::m_static = 0 ;void test01 () { int MyTest::*ptr1 = &MyTest::m_common; MyTest t1; t1.*ptr1 = 20 ; cout << "address: " << ptr1 << ", value: " << t1.m_common << endl; } void test02 () { int *ptr1 = &MyTest::m_static; *ptr1 = 40 ; cout << "address: " << ptr1 << ", value: " << *ptr1 << endl; } int main () { test01 (); test02 (); return 0 ; }
程序运行输出的结果如下:
1 2 address: 1, value: 20 address: 0x55f59236e194, value: 40
成员函数返回自身对象的引用 成员函数适合返回自身对象的引用的场景
返回当前对象(*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 指针。