其他分享
首页 > 其他分享> > 类与对象 —— 对象特性

类与对象 —— 对象特性

作者:互联网

 对象的初始化和清理

4.2.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     person()
 7     {
 8         cout << "person的构造函数" << endl;
 9     }
10     ~person()
11     {
12         cout << "person的析构函数函数" << endl;
13     }
14 };
15 //构造与析构都必须有实现,如果我们不提供,编译器会自动提供一个空实现的构造与析构·
16 void test01()
17 {
18     person p;//栈区函数,函数结束后会自动重置
19 }
20 
21 int main()
22 {
23     //test01();
24     person p;
25     system("pause");//再按任意键之前main函数不会结束所以析构函数不会出现
26     return 0;
27 }

 

 

 

 1 #include <iostream>
 2 using namespace std;
 3 class person
 4 {
 5 public:
 6     person()
 7     {
 8         cout << "person的构造函数" << endl;
 9     }
10     ~person()
11     {
12         cout << "person的析构函数函数" << endl;
13     }
14 };
15 //构造与析构都必须有实现,如果我们不提供,编译器会自动提供一个空实现的构造与析构·
16 void test01()
17 {
18     person p;//栈区函数,函数结束后会自动重置
19 }
20 
21 int main()
22 {
23     test01();//执行完后析构函数将自动出现
24     //person p;
25     system("pause");
26     return 0;
27 }

 

 

 

构造函数分类与三种输出方法:
分类与括号法:
 1 #include <iostream>
 2 using namespace std;
 3 class person
 4 {
 5 public:
 6     //构造函数按参数分为: 有参构造和无参构造(默认构造)
 7    //普通构造函数
 8     person()
 9     {
10         cout << "person 无参构造函数" << endl;
11     }
12     person(int a)
13     {
14         age = a;
15         cout << "person 有参构造函数" << endl;
16     }
17     //构造函数按类型分为: 普通构造和拷贝构造
18     //拷贝构造
19     person(const person& p)//const保证不修改原函数,用引用的方式是因为我们不是调用原函数
20     {
21         //将传入的人的所有的属性拷贝到当前对象上
22         age = p.age;
23         cout << "person 拷贝构造函数" << endl;
24     };
25     int age;
26 };
27 void test01()
28 {
29     //1.括号法
30     person p1;//无参构造函数
31    /* 注意事项1
32         调用默认构造函数时不要加()
33         因为编译器会认为是一个函数声明
34     person p1();
35     void fuc() 这就是函数声明;*/
36     person p2(10);//有参构造函数
37     person p3(p1);//拷贝构造函数 里面是p1 p2均可
38     cout << "p2年龄为" << p2.age << endl;
39     cout << "p3年龄为" << p3.age << endl;
40 }
41 int main()
42 {
43     test01();
44 }

 

 

 显示法:

1  
2    // 显示法
3     person p1;
4     person p2 = person(10);//与括号法调用方式 :person p2(10);对比
5     person p3 = person(p2);//与括号法调用方式 :person p3(p2);对比

匿名对象:

 1 #include <iostream>
 2 using namespace std;
 3 class person
 4 {
 5 public:
 6     //构造函数按参数分为: 有参构造和无参构造(默认构造)
 7    //普通构造函数
 8     person()
 9     {
10         cout << "person 无参构造函数" << endl;
11     }
12     person(int a)
13     {
14         age = a;
15         cout << "person 有参构造函数" << endl;
16     }
17     //构造函数按类型分为: 普通构造和拷贝构造
18     //拷贝构造
19     person(const person& p)//const保证不修改原函数,用引用的方式是因为我们不是调用原函数
20     {
21         //将传入的人的所有的属性拷贝到当前对象上
22         age = p.age;
23         cout << "person 拷贝构造函数" << endl;
24     };
25     ~person()
26     {
27         cout << "person 析构函数调用" << endl;
28     }
29     int age;
30 };
31 void test01()
32 {
33  person(10);//匿名对象 特点:当前执行结束后,系统会立即回收掉匿名对象
34     cout << "aaa";
35 }
36 int main()
37 {
38     test01();
39 }

 

 

 注意事项二 :不要用拷贝构造函数初始化匿名对象

 

 

 隐式转换法

 1 person p4 = 25;//相当于写了 person p4 = person(10);有参构造函数

2 person p5 = p4;//拷贝构造函数 

 

 

拷贝构造函数调用时机

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

 1 #include <iostream>
 2 using namespace std;
 3 class person
 4 {
 5 public:
 6     person()
 7     {
 8         cout << "person 的无参构造函数的调用" << endl;
 9     }
10     person(int age)
11     {
12         m_age = age;
13         cout << "person 的有参构造函数的调用" << endl;
14         cout << "参构造函数中的m_age:" << m_age << endl;
15     }
16     ~person()
17     {
18         cout << "person 的析构函数的调用" << endl;
19     }
20     person(const person& p)//传入类的别名并且限制修改类中的元素和修饰类的地址
21     {
22         cout << "person 的拷贝构造函数的调用" << endl;
23         //m_age = p.m_age;//将
24         //cout << "拷贝构造函数中的" << m_age << endl;
25         cout << "拷贝构造函数中的 p.m_age:" << p.m_age << endl;
26     }
27     int m_age;
28 };
29 //使用一个已经创建完毕的对象来初始化一个新对象
30 void test01()
31 {
32     person p1(20);
33     person p2(p1);
34 }
35 //2. 值传递(给一个函数的参数进行传值)的方式给函数参数传值
36 //相当于Person p1 = p;
37 void dowork(person p)
38 {
39 
40 }
41 void test02()
42 {
43     person p;
44     dowork(p);//值传递的本质是给原函数拷贝一个副本出来输出,这符合拷贝构造函数的调用条件
45 }
46 //3. 以值方式返回局部对象
47 person dowork2()
48 {
49     person p1;//再执行完之后会释放掉,所以会触发析构函数调用
50     cout << (int*)&p1 << endl;
51     return p1;//不会返回person p1中的p1,而是拷贝一份返回出去,这符合拷贝构造函数调用条件
52 }
53 void test03()
54 {
55     person p = dowork2();
56     cout << (int*)&p << endl;
57 }
58 int main()
59 {
60     //test01();
61     //test02();
62     test03();
63 }
64 
65  

 

构造函数调用规则

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

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

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

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

构造函数调用规则如下:

 

 

 

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

深拷贝与浅拷贝

深浅拷贝是面试经典问题,也是常见的一个坑

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

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

 1 #include <iostream>
 2 using namespace std;
 3 class person
 4 {
 5 public:
 6     person()
 7     {
 8         cout << "person的默认构造函数调用" << endl;
 9     }
10     person(int age ,int height)
11     {
12         m_age = age;
13         m_height = new int(height);
14         cout << "person的有参构造函数调用" << endl;
15     }
16     int m_age;
17     int* m_height;
18     ~person()//析构代码的用途 : 释放构造函数在堆区的内存
19     {
20         if (m_height != NULL)
21         {
22             delete m_height;
23             m_height = NULL;
24         }
25         cout << "person的析构函数调用" << endl;
26     }
27     person(const person& p)
28     {
29         cout << "person拷贝构造函数调用的调用" << endl;
30         //系统会自动执行实现m_ age = p.m_age;m_height =p.m_height;
31         //但因为拷贝函数和原函数的height指向同一个地址开辟在堆区,释放时会有冲突,所以我们需要
32         //深拷贝操作
33         m_height = new int(*p.m_height);
34     }
35 };
36 void test01()
37 {
38     person p1(18,160);
39     cout << "p1 age is :" << p1.m_age  << ",and his height is : " << *p1.m_height<< endl;
40     person p2(p1);
41     cout << "p2 age is :" << p2.m_age << ",and his height is : " <<* p2.m_height << endl;
42 }
43 int main()
44 {
45     test01();
46 }

 

 

 

 

 

 

 初始化列表

作用:

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

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

初始化数据成员与对数据成员赋值的含义是什么?有什么区别?

首先把数据成员按类型分类并分情况说明:

注意点:

初始化列表的成员初始化顺序:

C++ 初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。

1 class CMyClass {
2     CMyClass(int x, int y);
3     int m_x;
4     int m_y;
5 };
6 
7 CMyClass::CMyClass(int x, int y) : m_y(y), m_x(m_y)
8 {
9 };

你可能以为上面的代码将会首先做 m_y=I,然后做 m_x=m_y,最后它们有相同的值。但是编译器先初始化 m_x,然后是 m_y,,因为它们是按这样的顺序声明的。结果是 m_x 将有一个不可预测的值。有两种方法避免它,一个是总是按照你希望它们被初始化的顺序声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明的顺序罗列这些成员。这将有助于消除混淆。

 1 #include <iostream>
 2 using namespace std;
 3 class person
 4 {
 5 public:
 6     person(int a,int b ,int c) :m_a(a), m_b(b), m_c(c)
 7     {
 8 
 9     }
10     int m_a;
11     int m_b;
12     int m_c;
13 };
14 void test01()
15 {
16     person p(30,20,10);
17     cout << "m_a = " << p.m_a << endl
18         << "m_b = " << p.m_b << endl
19         << "m_c = " << p.m_c << endl;
20 
21 }
22 int main()
23 {
24     test01();
25 }

 

 

 类对象作为类成员

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

例如:

 1 class A {}

2 class B

3 {  A a;  } 

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

那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后?

 1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4 class phone
 5 {
 6 public:
 7     string m_pname;//创建一个名为m_pname 的string型变量
 8     phone(string pname)
 9     {
10         m_pname = pname;
11         cout << "phone的构造函数调用" << endl;
12     }
13     ~phone()
14     {
15         cout << "phone的析构函数调用" << endl;
16     }
17 };
18 class person
19 {
20 public:
21     person(string name,string pname):m_name(name), m_phname(pname)
22     {
23         cout << "person的构造函数调用" << endl;
24     }
25     ~person()
26     {
27         cout << "person的析构函数调用" << endl;
28     }
29     phone m_phname;//m_phname 为phone类的变量
30     string m_name;
31 };
32 void test01()
33 {
34     //当类中成员是其他类对象时,我们称该成员为 对象成员
35     //构造的顺序是 :先调用对象成员的构造,再调用本类构造
36     //析构顺序与构造相反,因为栈区有先进后出的原则
37     person p("张三","iphone");
38     cout << "名字 :" << p.m_name << ",手机 :" << p.m_phname.m_pname << endl;
39 }
40 int main()
41 {
42     test01();
43 }

 

 

 静态成员

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

静态成员分为:

标签:初始化,对象,成员,特性,person,int,构造函数
来源: https://www.cnblogs.com/zaiyewujiang/p/16629571.html