C++面向对象基础--对象的初始化和清理

1.构造函数和析构函数

对象的初始化和清理是两个非常重要的安全问题

一个对象或者变量没有初始状态,对其使用后果是未知

​同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题

C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作

对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供

编译器提供的构造函数和析构函数是空实现。 

构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。 

构造函数语法:类名(){}

1. 构造函数,没有返回值也不写void
2. 函数名称与类名相同
3. 构造函数可以有参数,因此可以发生重载
4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数语法:~类名(){}

1. 析构函数,没有返回值也不写void
2. 函数名称与类名相同,在名称前加上符号 ~
3. 析构函数不可以有参数,因此不可以发生重载
4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

示例代码:

1 #include<iostream> 2 using namespace std; 3 class Person 4 { 5 public: 6 //构造函数 7 Person() 8 { 9 cout << "Person的构造函数调用" << endl; 10 } 11 //析构函数 12 ~Person() 13 { 14 cout << "Person的析构函数调用" << endl; 15 } 16 17 }; 18 19 void test01() 20 { 21 Person p; 22 } 23 24 int main() 25 { 26 27 test01(); 28 29 system("pause"); 30 31 return 0; 32 }

运行结果:

2.构造函数的分类及调用

两种分类方式:

​按参数分为: 有参构造和无参构造

按类型分为: 普通构造和拷贝构造

三种调用方式:

​ (1)括号法

​ (2)显示法

​ (3)隐式转换法

1 #include<iostream> 2 using namespace std; 3 //1、构造函数分类 4 // 按照参数分类分为 有参和无参构造 无参又称为默认构造函数 5 // 按照类型分类分为 普通构造和拷贝构造 6 7 class Person 8 { 9 public: 10 //无参(默认)构造函数 11 Person() 12 { 13 cout << "无参构造函数!" << endl; 14 } 15 //有参构造函数 16 Person(int a) 17 { 18 age = a; 19 cout << "有参构造函数!" << endl; 20 } 21 //拷贝构造函数 22 Person(const Person& p) 23 { 24 age = p.age; 25 cout << "拷贝构造函数!" << endl; 26 } 27 //析构函数 28 ~Person() 29 { 30 cout << "析构函数!" << endl; 31 } 32 public: 33 int age; 34 }; 35 36 //2、构造函数的调用 37 //调用无参构造函数 38 void test01() 39 { 40 Person p; //调用无参构造函数 41 } 42 43 //调用有参的构造函数 44 void test02() 45 { 46 47 //2.1 括号法,常用 48 Person p1(10); 49 //注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明 50 //Person p2(); 51 52 //2.2 显式法 53 Person p2 = Person(10); 54 Person p3 = Person(p2); 55 //Person(10)单独写就是匿名对象 当前行结束之后,马上析构 56 57 //2.3 隐式转换法 58 Person p4 = 10; // Person p4 = Person(10); 59 Person p5 = p4; // Person p5 = Person(p4); 60 61 //注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明 62 //Person p5(p4); 63 } 64 65 int main() 66 { 67 68 test01(); 69 //test02(); 70 71 system("pause"); 72 73 return 0; 74 }

运行结果:

3.拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况

(1)使用一个已经创建完毕的对象来初始化一个新对象
(2)值传递的方式给函数参数传值
(3)以值方式返回局部对象

示例代码:

1 #include<iostream> 2 using namespace std; 3 class Person 4 { 5 public: 6 Person() 7 { 8 cout << "无参构造函数!" << endl; 9 mAge = 0; 10 } 11 Person(int age) 12 { 13 cout << "有参构造函数!" << endl; 14 mAge = age; 15 } 16 Person(const Person& p) 17 { 18 cout << "拷贝构造函数!" << endl; 19 mAge = p.mAge; 20 } 21 //析构函数在释放内存之前调用 22 ~Person() 23 { 24 cout << "析构函数!" << endl; 25 } 26 public: 27 int mAge; 28 }; 29 30 //1. 使用一个已经创建完毕的对象来初始化一个新对象 31 void test01() 32 { 33 34 Person man(100); //p对象已经创建完毕 35 Person newman(man); //调用拷贝构造函数 36 Person newman2 = man; //拷贝构造 37 38 //Person newman3; 39 //newman3 = man; //不是调用拷贝构造函数,赋值操作 40 } 41 42 //2. 值传递的方式给函数参数传值 43 //相当于Person p1 = p; 44 void doWork(Person p1) {} 45 void test02() 46 { 47 Person p; //无参构造函数 48 doWork(p); 49 } 50 51 //3. 以值方式返回局部对象 52 Person doWork2() 53 { 54 Person p1; 55 cout << (int*)&p1 << endl; 56 return p1; 57 } 58 59 void test03() 60 { 61 Person p = doWork2(); 62 cout << (int*)&p << endl; 63 } 64 65 66 int main() 67 { 68 69 //test01(); 70 //test02(); 71 test03(); 72 73 system("pause"); 74 75 return 0; 76 }

运行结果:

4.构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数

1.默认构造函数(无参,函数体为空)

2.默认析构函数(无参,函数体为空)

3.默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造

如果用户定义拷贝构造函数,c++不会再提供其他构造函数

示例代码:

1 #include<iostream> 2 using namespace std; 3 class Person 4 { 5 public: 6 //无参(默认)构造函数 7 Person() 8 { 9 cout << "无参构造函数!" << endl; 10 } 11 //有参构造函数 12 Person(int a) 13 { 14 age = a; 15 cout << "有参构造函数!" << endl; 16 } 17 //拷贝构造函数 18 Person(const Person& p) 19 { 20 age = p.age; 21 cout << "拷贝构造函数!" << endl; 22 } 23 //析构函数 24 ~Person() 25 { 26 cout << "析构函数!" << endl; 27 } 28 public: 29 int age; 30 }; 31 32 void test01() 33 { 34 Person p1(18); 35 //如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作 36 Person p2(p1); 37 38 cout << "p2的年龄为: " << p2.age << endl; 39 } 40 41 void test02() 42 { 43 //如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造 44 Person p1; //此时如果用户自己没有提供默认构造,会出错 45 Person p2(10); //用户提供的有参 46 Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供 47 48 //如果用户提供拷贝构造,编译器不会提供其他构造函数 49 Person p4; //此时如果用户自己没有提供默认构造,会出错 50 Person p5(10); //此时如果用户自己没有提供有参,会出错 51 Person p6(p5); //用户自己提供拷贝构造 52 } 53 54 int main() 55 { 56 57 test01(); 58 59 system("pause"); 60 61 return 0; 62 }

运行结果:

5.深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

示例代码:

1 #include<iostream> 2 using namespace std; 3 class Person 4 { 5 public: 6 //无参(默认)构造函数 7 Person() 8 { 9 cout << "无参构造函数!" << endl; 10 } 11 //有参构造函数 12 Person(int age, int height) 13 { 14 15 cout << "有参构造函数!" << endl; 16 17 m_age = age; 18 m_height = new int(height); 19 20 } 21 //拷贝构造函数 22 Person(const Person& p) 23 { 24 cout << "拷贝构造函数!" << endl; 25 //如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题 26 m_age = p.m_age; 27 m_height = new int(*p.m_height); 28 29 } 30 31 //析构函数 32 ~Person() 33 { 34 cout << "析构函数!" << endl; 35 if (m_height != NULL) 36 { 37 delete m_height; 38 } 39 } 40 public: 41 int m_age; 42 int* m_height; 43 }; 44 45 void test01() 46 { 47 Person p1(18, 180); 48 49 Person p2(p1); 50 51 cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl; 52 53 cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl; 54 } 55 56 int main() 57 { 58 59 test01(); 60 61 system("pause"); 62 63 return 0; 64 }

运行结果:

总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

6.初始化列表

作用:

C++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)... {}

示例代码:

1 #include<iostream> 2 using namespace std; 3 class Person 4 { 5 public: 6 7 ////传统方式初始化 8 //Person(int a, int b, int c) 9 //{ 10 // m_A = a; 11 // m_B = b; 12 // m_C = c; 13 //} 14 15 //初始化列表方式初始化 16 Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {} 17 void PrintPerson() 18 { 19 cout << "mA:" << m_A << endl; 20 cout << "mB:" << m_B << endl; 21 cout << "mC:" << m_C << endl; 22 } 23 private: 24 int m_A; 25 int m_B; 26 int m_C; 27 }; 28 29 int main() 30 { 31 32 Person p(1, 2, 3); 33 p.PrintPerson(); 34 35 36 system("pause"); 37 38 return 0; 39 }

运行结果:

7.类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为对象成员

例如:

1 class A {} 2 class B 3 { 4 A a; 5 }

B类中有对象A作为成员,A为对象成员

示例代码:

1 #include<iostream> 2 using namespace std; 3 class Phone 4 { 5 public: 6 Phone(string name) 7 { 8 m_PhoneName = name; 9 cout << "Phone构造" << endl; 10 } 11 12 ~Phone() 13 { 14 cout << "Phone析构" << endl; 15 } 16 17 string m_PhoneName; 18 19 }; 20 21 22 class Person 23 { 24 public: 25 26 //初始化列表可以告诉编译器调用哪一个构造函数 27 Person(string name, string pName) :m_Name(name), m_Phone(pName) 28 { 29 cout << "Person构造" << endl; 30 } 31 32 ~Person() 33 { 34 cout << "Person析构" << endl; 35 } 36 37 void playGame() 38 { 39 cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手机! " << endl; 40 } 41 42 string m_Name; 43 Phone m_Phone; 44 45 }; 46 void test01() 47 { 48 //当类中成员是其他类对象时,我们称该成员为 对象成员 49 //构造的顺序是 :先调用对象成员的构造,再调用本类构造 50 //析构顺序与构造相反 51 Person p("张三", "苹果X"); 52 p.playGame(); 53 54 } 55 56 57 int main() 58 { 59 60 test01(); 61 62 system("pause"); 63 64 return 0; 65 }

运行结果:

8.静态成员

静态成员就是在成员变量成员函数前加上关键字static,称为静态成员

静态成员分为静态成员变量和静态成员函数

静态成员变量包括:

(1)所有对象共享同一份数据
(2)在编译阶段分配内存
(3)类内声明,类外初始化
静态成员函数包括:
(1)所有对象共享同一个函数
(2)静态成员函数只能访问静态成员变量

示例代码(静态成员变量):

1 #include<iostream> 2 using namespace std; 3 class Person 4 { 5 public: 6 7 static int m_A; //静态成员变量 8 9 //静态成员变量特点: 10 //1 在编译阶段分配内存 11 //2 类内声明,类外初始化 12 //3 所有对象共享同一份数据 13 14 private: 15 static int m_B; //静态成员变量也是有访问权限的 16 }; 17 int Person::m_A = 10; 18 int Person::m_B = 10; 19 20 void test01() 21 { 22 //静态成员变量两种访问方式 23 24 //1、通过对象 25 Person p1; 26 p1.m_A = 100; 27 cout << "p1.m_A = " << p1.m_A << endl; 28 29 Person p2; 30 p2.m_A = 200; 31 cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据 32 cout << "p2.m_A = " << p2.m_A << endl; 33 34 //2、通过类名 35 cout << "m_A = " << Person::m_A << endl; 36 37 38 //cout << "m_B = " << Person::m_B << endl; //私有权限访问不到 39 } 40 41 int main() 42 { 43 44 test01(); 45 46 system("pause"); 47 48 return 0; 49 }

运行结果:

示例代码(静态成员函数):

1 #include<iostream> 2 using namespace std; 3 class Person 4 { 5 public: 6 7 //静态成员函数特点: 8 //1 程序共享一个函数 9 //2 静态成员函数只能访问静态成员变量 10 11 static void func() 12 { 13 cout << "func调用" << endl; 14 m_A = 100; 15 //m_B = 100; //错误,不可以访问非静态成员变量 16 } 17 18 static int m_A; //静态成员变量 19 int m_B; // 20 private: 21 22 //静态成员函数也是有访问权限的 23 static void func2() 24 { 25 cout << "func2调用" << endl; 26 } 27 }; 28 int Person::m_A = 10; 29 30 31 void test01() 32 { 33 //静态成员变量两种访问方式 34 35 //1、通过对象 36 Person p1; 37 p1.func(); 38 39 //2、通过类名 40 Person::func(); 41 42 43 //Person::func2(); //私有权限访问不到 44 } 45 46 int main() 47 { 48 49 test01(); 50 51 system("pause"); 52 53 return 0; 54 }

运行结果:


__EOF__

本文作者神楽桜KaguraSakura
本文链接https://www.cnblogs.com/KaguraSakura/p/13385189.html
关于博主:hello~好久不见,喜欢的话点个赞吧
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   神楽桜KaguraSakura  阅读(407)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示