类和对象的基本概念

Sandals-little / 2023-05-14 / 原文

封装:

属性和行为作为一个整体,来表现各种事物。

将属性和行为加以权限控制(private,public,protected)

一些术语:

属性(成员属性,成员变量),行为(成员函数,成员方法);统称为成员。

实例化(通过一个类,创建一个对象的过程)

访问权限:

当一个形参是函数对象时,该函数体内部不可以访问该对象的私有属性。

当一个类的成员是另外一个类的对象时(或则说该成员的类型是另外一个类),则该类不能访问它的成员的私有属性(也就是另外一个类的私有属性),除非两者是友元的关系。

控制私有成员的读写权限:

(1)读和写:

(2)只读:

(3)加一个判断:

(4)只写:

构造和析构函数 :

只会调用一次

构造函数在对象创建处会被调用,析构函数在对象所在作用域结束后会被调用

有参构造:

就是构造函数的括号中含有参数,有参构造也可以在函数体内对参数进行一些操作。最重要的目的是给成员属性赋初值。

拷贝构造:

只有真正的在拷贝构造的函数体中显式赋值拷贝的对象的成员才会拷贝给被拷贝对象

但是当自己不写拷贝构造函数时,系统会自动将拷贝的对象的成员属性传递给被拷贝对象的成员属性。

 1     //Point(const Person& p)
 2     //{
 3     //    m_Age = p.m_Age;
 4     //    cout << "Person的拷贝构造函数调用" << endl;
 5     //}
 6 
 7 
 8 void test01()
 9 {
10     Point p;
11     p.m_Age = 18;
12     Point p2(p);
13     cout << "p2的年龄为:" << p2.m_Age << endl;
14 }
15 int main()
16 {
17     test01();
18 }

 

创建对象的方法:

括号法创建对象:

显示法:

匿名对象:

打印结果说明函数没有执行完毕,匿名函数就被释放掉了

隐式转换法:

拷贝构造何时会被调用:

函数以值传递的方式进行参数传递的时候,如果形参是对象的话,就调用了拷贝构造,当实参给形参初始化的时候相当于隐式转换法

构造函数调用规则:

构造函数按参数分类可以分为有参构造和无参构造,按类型分类可以分为普通构造和拷贝构造。

默认情况下,C++编译至少给一个类添加3个函数
默认构造函数(无参,函数体为空)
默认析构函数(无参,函数体为空)
默认拷贝构造函数,对属性进行值传递

构造函数调用规则如下:

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

(编译器不会提供意味着必须要自己写,不写就用不了。编译器提供意味着自己不写也可以用)

析构函数的作用:

 1 class Person
 2 {
 3 public:
 4     Person()
 5     {
 6         cout << "Person的默认构造函数调用" << endl;
 7     }
 8     Person(int age,int height)
 9     {
10         cout << "Person的有参构造函数调用" << endl;
11         m_Age = age;
12         m_Height = new int(height);
13     }
14     ~Person()
15     {
16         //析构代码,将堆区开辟数据做释放操作
17         if (m_Height != NULL)
18         {
19             delete m_Height;
20             m_Height = NULL;
21             cout << "Person的析构函数调用" << endl;
22         }
23     }
24     int m_Age;
25     int* m_Height;
26 };

浅拷贝和深拷贝:

 

如果利用编译器提供的拷贝构造函数,就会做浅拷贝操作。

 

浅拷贝出现问题在于:对象中成员属性具有指针类型,并且该指针指向一块堆区的内存,这样在做拷贝构造时,当调用两个析构函数的时候就会造成堆区的内存重复释放。

也就是说P2会把P1所有内容原封不动的拷贝过来,当P2析构时释放掉m_Height所指向的堆区内存时后P1再来释放这块内存就会非法操作。

 

 

 1 class Person
 2 {
 3 public:
 4     Person()
 5     {
 6         cout << "Person的默认构造函数调用" << endl;
 7     }
 8     Person(int age,int height)
 9     {
10         cout << "Person的有参构造函数调用" << endl;
11         m_Age = age;
12         m_Height = new int(height);
13     }
14     //Person(const Person& p)
15     //{
16     //    m_Age = p.m_Age;
17     //    cout << "Person的拷贝构造函数调用" << endl;
18     //}
19     ~Person()
20     {
21         //析构代码,将堆区开辟数据做释放操作
22         if (m_Height != NULL)
23         {
24             delete m_Height;
25             m_Height = NULL;
26             cout << "Person的析构函数调用" << endl;
27         }
28     }
29     int m_Age;
30     int* m_Height;
31 };
32 void test01()
33 {
34     Person p(18,168);
35     Person p2(p);
36     cout << "p的年龄为:" << p.m_Age << "身高为:" << *p.m_Height << endl;
37     cout << "p2的年龄为:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;
38 }
39 int main()
40 {
41     test01();
42 }

 解决方法是进行深拷贝,重新申请一块堆区内存,让P2中的指针指向这块堆区内存。

 1 class Person
 2 {
 3 public:
 4     Person()
 5     {
 6         cout << "Person的默认构造函数调用" << endl;
 7     }
 8     Person(int age,int height)
 9     {
10         cout << "Person的有参构造函数调用" << endl;
11         m_Age = age;
12         m_Height = new int(height);
13     }
14     Person(const Person& p)
15     {
16         cout << "Person的拷贝构造函数调用" << endl;
17         m_Age = p.m_Age;
18         //当我们把拷贝构造函数注释掉的时候,编译器会自动进行以下程序,也就是说只要涉及到拷贝对象拷贝给被拷贝对象指针类型的成员函数的时候就是浅拷贝
19         //m_Height = p.m_Height;
20         //深拷贝操作
21         m_Height = new int(*p.m_Height);
22     }
23     ~Person()
24     {
25         //析构代码,将堆区开辟数据做释放操作
26         if (m_Height != NULL)
27         {
28             delete m_Height;
29             m_Height = NULL;
30             cout << "Person的析构函数调用" << endl;
31         }
32     }
33     int m_Age;
34     int* m_Height;
35 };
36 void test01()
37 {
38     Person p(18,168);
39     Person p2(p);
40     cout << "p的年龄为:" << p.m_Age << "身高为:" << *p.m_Height << endl;
41     cout << "p2的年龄为:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;
42 }
43 int main()
44 {
45     test01();
46 }