ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

类与对象 —— 对象特性

2022-08-29 12:30:48  阅读:176  来源: 互联网

标签:初始化 对象 成员 特性 person int 构造函数


 对象的初始化和清理

  • 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全
  • C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置。

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.默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

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

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

 

 

 

 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)... {}

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

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

  • 1.内置数据类型,复合类型(指针,引用)- 在成员初始化列表和构造函数体内进行,在性能和结果上都是一样的
  • 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,称为静态成员

静态成员分为:

  • 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
    •  1 #include <iostream>
       2 using namespace std;
       3 class person
       4 {
       5 public:
       6     //所有对象都共享同一份数据
       7     // 编译阶段就分配了内存
       8     //类内声明,类外初始化操作
       9     static int m_a;//类内声明,编译阶段分配了内存
      10     //静态成员变量也是有访问权限的
      11 private:
      12     static int m_b;
      13 };
      14 int person::m_a = 100;//类外初始化
      15 int person::m_b = 200;
      16 void test01()
      17 {
      18     person p;
      19     cout <<"修改前p.m_a :" <<  p.m_a << endl;
      20     person p2;
      21     p2.m_a = 200;
      22     cout << "利用p2修改后p.m_a :" << p.m_a << endl;//所有对象都共享同一份数据 ,当一个对象对数据进行修改,其他的对象共享的数据也变成了修改后的数据
      23     cout << "p2.m_a :" << p2.m_a << endl;
      24 }
      25 void test02()
      26 {
      27     //静态成员变量 不属于某一个对象上 所有对象都共享同一份数据
      28     //因此静态成员变量有两种的访问方式
      29     
      30     //第一种:通过对象进行访问
      31    /* person p;
      32     cout << p.m_a << endl;*/
      33     //第二种:通过类目进行访问
      34     cout << person::m_a << endl;
      35    // cout << person::m_b << endl; 类外访问不到私有的静态成员变量
      36 }
      37 int main()
      38 {
      39     //test01();
      40     test02();
      41 }


       

       

      • 静态成员函数
        • 所有对象共享同一个函数
        • 静态成员函数只能访问静态成员变量

       

       1 #include <iostream>
       2 using namespace std;
       3 //静态成员函数
       4 //所有对象共享同一个函数
       5 //静态成员函数只能访问静态成员变量
       6 class person
       7 {
       8 public:
       9     //静态成员函数
      10     static void func()
      11     {
      12         m_a = 100;//静态成员函数可以访问静态成员变量,所有对象共享的
      13         m_b = 200;//静态成员函数不可以访问非静态成员变量,无法区分到底是哪个对象的m_b属性;运行时会报错
      14         cout << "static void func()的调用" << endl;
      15     }
      16     static int m_a;//静态成员变量
      17 private:
      18     static void func2()
      19     {
      20         cout << "static void func2()的调用" << endl;
      21     }
      22 };
      23 int person::m_a = 0;//静态成员变量
      24 int m_b = 1;//非静态成员变量
      25 //有两种访问方式
      26 void test01()
      27 {
      28     //通过对象进行访问
      29     person p;
      30     p.func();
      31     //通过类名进行访问
      32     person::func();
      33     //person::func2();//错误操作,私有作用域下类外不可访问
      34 }
      35 int main()
      36 {
      37     test01();
      38 }

       

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有