C++ 类的封装性深度解析

一.类的封装与类成员的作用域

类通常可以分为使用方式和内部细节两部分, 类的封装机制使得使用方式和内部细节相分离。

C++中通过定义类成员的访问级别实现封装机制,pubilc成员可以在类的内部和外部访问和调用,private成员只能在类的内部被访问和调用。

复制代码
 1 #include <stdio.h>
 2  
 3 int i = 1;
 4  
 5 struct Test
 6 {
 7 private:
 8     int i;
 9  
10 public:
11     int j;
12         
13     int getI()
14     {
15         i = 3;
16         
17         return i;
18     }
19 };
20  
21 int main()
22 {
23     int i = 2;
24     
25     Test test;
26     
27     test.j = 4;
28     
29     printf("i = %d\n", i);              // i = 2;
30     printf("::i = %d\n", ::i);          // ::i = 1; 全局全局的
31     // printf("test.i = %d\n", test.i);    // Error
32     printf("test.j = %d\n", test.j);    // test.j = 4
33     printf("test.getI() = %d\n", test.getI());  // test.getI() = 3
34     
35     return 0;
36 }
复制代码

二.class和struct的区别

在使用struct定义类时,所有成员的默认访问级别为public(公开的)。

在用class定义类时,所有成员的默认访问级别为private(私有的)。

复制代码
 1 #include <stdio.h>
 2  
 3 //在用struct定义类时,所有成员的默认访问级别为pubil
 4 struct A
 5 {
 6     // defualt to public
 7     int i;
 8     // defualt to public
 9     int getI()
10     {
11         return i;
12     }
13 };
14  
15 //用class定义类时,所有成员的默认访问级别为private私密
16 class B
17 {
18 public19     // defualt to private
20     int i;
21     // defualt to private
22     int getI()
23     {
24         return i;
25     }
26 };
27  
28 int main()
29 {
30     A a;
31     B b;
32     
33     a.i = 4;
34     
35     printf("a.getI() = %d\n", a.getI());
36     
37     b.i = 4;
38     
39     printf("b.getI() = %d\n", b.getI());
40     
41     return 0;
42 }
复制代码

三.构造函数

类的构造函数用于对象的初始化,构造函数在对象定义时自动被调用,构造没有任何返回类型的声明。

复制代码
 1 #include <stdio.h>
 2  
 3 class Test
 4 {
 5 private:
 6     int i;
 7     int j;
 8 public:
 9     int getI() { return i; }
10     int getJ() { return j; }
11     Test()
12     {
13         printf("Test() Begin\n");
14         
15         i = 1;
16         j = 2;
17         
18         printf("Test() End\n");
19     }
20 };
21  
22 Test gt;  //定义对象时,构造函数自动被执行
23  
24 int main()
25 {
26     printf("gt.i = %d\n", gt.getI());  //1
27     printf("gt.j = %d\n", gt.getJ());  //2
28     
29     Test t1;//定义对象时,构造函数自动被执行
30     
31     printf("t1.i = %d\n", t1.getI());  //1
32     printf("t1.j = %d\n", t1.getJ());  //2
33     
34     Test* pt = new Test;  //定义对象时,构造函数自动被执行
35     
36     printf("pt->i = %d\n", pt->getI());  //1
37     printf("pt->j = %d\n", pt->getJ());  //2
38     
39     delete pt;
40     
41     return 0;
42 }
复制代码

对象构造的顺序:

局部对象:当程序执行流到达对象的定义语句时进行构造。

对于堆对象:当程序执行流到达new语句时创建对象,使用new创建对象将自动触发构造函数的调用。

复制代码
 1 #include <stdio.h>
 2  
 3 class Test
 4 {
 5 private:
 6     int mi;
 7 public:
 8     Test(int i)
 9     {
10         mi = i;
11         printf("Test(int i): %d\n", mi);
12     }
13     Test(const Test& obj)
14     {
15         mi = obj.mi;
16         printf("Test(const Test& obj): %d\n", mi);
17     }
18     int getMi()
19     {
20         return mi;
21     }
22 };
23  
24 int main()
25 {
26     int i = 0;
27     Test* a1 = new Test(i); // Test(int i): 0
28  
29     while( ++i < 10 )
30         if( i % 2 )
31             new Test(i); // Test(int i): 1, 3, 5, 7, 9
32  
33     if( i < 4 )
34         new Test(*a1);
35     else
36         new Test(100); // Test(int i): 100
37  
38     return 0;
39 }
复制代码

四.带参构造函数,拷贝构造函数

带参构造函数:构造函数可以根据需要定义参数,一个类中可以存在多个重载的构造函数。

复制代码
 1 #include <stdio.h>
 2  
 3 class Test
 4 {
 5 public:
 6     Test() 
 7     { 
 8         printf("Test()\n");
 9     }
10     Test(int v) 
11     { 
12         printf("Test(int v), v = %d\n", v);
13     }
14 };
15  
16 int main()
17 {
18     Test t;      // 调用 Test()
19     Test t1(1);  // 调用 Test(int v)
20     Test t2 = 2; // 调用 Test(int v)
21  
22     int i(100);
23  
24     printf("i = %d\n", i);
25  
26     return 0;
27 }
复制代码

拷贝构造函数:

复制代码
 1 #include <stdio.h>
 2  
 3 class Test
 4 {
 5 private:
 6     int i;
 7     int j;
 8 public:
 9     int getI()
10     {
11         return i;
12     }
13     int getJ()
14     {
15         return j;
16     }
17     /*Test(const Test& t)//自己手工编写一个拷贝构造函数,居然编译不过。
18     {                    //编译器觉得我们自己提供一个拷贝构造函数,拷贝构造函数
19                          //也是构造函数,所以编译器觉得无需给我们提供构造函数了
20         i = t.i;         //所以就会编译错误。下面再编写无参的构造函数就不会出错。
21         j = t.j;
22     }
23     Test()
24     {
25     }*/
26 };
27  
28 int main()
29 {
30     Test t1;
31     Test t2 = t1;
32     
33     printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ());//t1.i=134513984, t1.j=-1079264296
34     printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ());//t1.i=134513984, t1.j=-1079264296
35     
36     return 0;
37 }
复制代码

五.析构函数

析构函数的功能和构造函数相反,构造函数是用来给对象做初始化的,析构函数是做对象的清理工作的。

析构函数的调用顺序:多个对象析构时析构顺序与构造顺序相反。

复制代码
 1 #include <stdio.h>
 2  
 3 class Member
 4 {
 5     const char* ms;
 6 public:
 7     Member(const char* s)
 8     {
 9         printf("Member(const char* s): %s\n", s);
10  
11         ms = s;
12     }
13     ~Member()
14     {
15         printf("~Member(): %s\n", ms);
16     }
17 };
18  
19 class Test
20 {
21     Member mA;
22     Member mB;
23 public:
24     Test() : mB("mB"), mA("mA")
25     {
26         printf("Test()\n");
27     }
28     ~Test()
29     {
30         printf("~Test()\n");
31     }
32 };
33  
34 Member gA("gA");
35  
36 int main()
37 {
38     Test t;
39  
40     return 0;
41 }
复制代码

结果:多个对象析构时析构顺序与构造顺序相反。

Member(const char* s): gA

Member(const char* s): mA

Member(const char* s): mB

Test()

~Test()

~Member(): mB

~Member(): mA

~Member(): gA

六.友元函数与友元类

友元关系发生在函数与类之间或者类与类之间,在类中以friend关键字声明友元,类的友元可以是其它类或者具体函数,友元不受类中访问级别的限制,友元可以直接访问具体类的所有成员,但是友元直接破坏了面向对象的封装性。

友元函数:

复制代码
 1 #include <stdio.h>
 2 #include <math.h>
 3  
 4 class Point
 5 {
 6     double x;
 7     double y;
 8 public:
 9     Point(double x, double y)
10     {
11         this->x = x;
12         this->y = y;
13     }
14  
15     double getX()
16     {
17         return x;
18     }
19  
20     double getY()
21     {
22         return y;
23     }
24  
25     friend double func(Point& p1, Point& p2);
26 };
27  
28 double func(Point& p1, Point& p2)
29 {
30     double ret = 0;
31  
32     ret = (p2.y - p1.y) * (p2.y - p1.y) +
33           (p2.x - p1.x) * (p2.x - p1.x);
34  
35     ret = sqrt(ret);
36  
37     return ret;
38 }
39  
40 int main()
41 {
42     Point p1(1, 2);
43     Point p2(10, 20);
44  
45     printf("p1(%f, %f)\n", p1.getX(), p1.getY());  //p1(1.000000, 2.000000)
46     printf("p2(%f, %f)\n", p2.getX(), p2.getY());  //p2(10.000000, 20.000000)
47     printf("|(p1, p2)| = %f\n", func(p1, p2));     //|(p1, p2)| = 20.124612
48  
49  
50     return 0;
51 }
复制代码

友元类:

友元类其实就是批量制造友元函数,类的友元可以是某个完整的类,类的所有的成员函数都是友元。

友元类中所有全部成员都成为了友元函数,相当于一次打了很多洞,极大破坏了面向对象。

复制代码
 1 #include <stdio.h>
 2  
 3 class ClassC
 4 {
 5     const char* n;
 6 public:
 7     ClassC(const char* n)
 8     {
 9         this->n = n;
10     }
11  
12     friend class ClassB;
13 };
14  
15 class ClassB
16 {
17     const char* n;
18 public:
19     ClassB(const char* n)
20     {
21         this->n = n;
22     }
23  
24     void getClassCName(ClassC& c)
25     {
26         printf("c.n = %s\n", c.n);
27     }
28  
29     friend class ClassA;
30 };
31  
32 class ClassA
33 {
34     const char* n;
35 public:
36     ClassA(const char* n)
37     {
38         this->n = n;
39     }
40  
41     void getClassBName(ClassB& b)
42     {
43         printf("b.n = %s\n", b.n);
44     }
45     /*
46     void getClassCName(ClassC& c)
47     {
48         printf("c.n = %s\n", c.n);
49     }
50     */
51 };
52  
53 int main()
54 {
55     ClassA A("A");
56     ClassB B("B");
57     ClassC C("C");
58  
59     A.getClassBName(B);  //B
60     B.getClassCName(C);  //C
61  
62     return 0;
63 }
复制代码

七.静态成员变量和静态成员函数

静态成员变量具有记忆性:

复制代码
 1 #include <stdio.h>
 2  
 3 class Test
 4 {
 5 private:
 6     static int cCount;
 7 public:
 8     Test()
 9     {
10         cCount++;
11     }
12     ~Test()
13     {
14         --cCount;
15     }
16     int getCount()
17     {
18         return cCount;
19     }
20 };
21  
22 int Test::cCount = 0;
23  
24 Test gTest;
25  
26 int main()
27 {
28     Test t1;
29     Test t2;
30  
31     printf("count = %d\n", gTest.getCount());  //3
32     printf("count = %d\n", t1.getCount());     //3
33     printf("count = %d\n", t2.getCount());     //3
34  
35     Test* pt = new Test();
36  
37     printf("count = %d\n", pt->getCount());   //4
38  
39     delete pt;
40  
41     printf("count = %d\n", gTest.getCount());   //3
42  
43     return 0;
44 }
复制代码

静态成员函数:

可以通过类名直接访问公有静态成员函数。

复制代码
 1 #include <stdio.h>
 2  
 3 class Demo
 4 {
 5 private:
 6     int i;
 7 public:
 8     int getI();
 9     static void StaticFunc(const char* s);
10     static void StaticSetI(Demo& d, int v);
11 };
12  
13 int Demo::getI()
14 {
15     return i;
16 }
17  
18 void Demo::StaticFunc(const char* s)
19 {
20     printf("StaticFunc: %s\n", s);
21 }
22  
23 void Demo::StaticSetI(Demo& d, int v)
24 {
25     d.i = v;
26 }
27  
28 int main()
29 {
30     Demo::StaticFunc("main Begin...");
31  
32     Demo d;
33  
34     Demo::StaticSetI(d, 10);
35  
36     printf("d.i = %d\n", d.getI());  //d.i = 10
37  
38     Demo::StaticFunc("main End...");
39  
40     return 0;
41 }
复制代码

八.函数重载

类中的成员函数可以进行重载:

构造函数的重载,普通成员函数的重载,静态成员函数的重载。

重载的意义:扩展系统中已经存在的函数功能。

复制代码
 1 #include <stdio.h>
 2  
 3 class Test
 4 {
 5     int i;
 6 public:
 7     Test()
 8     {
 9         printf("Test::Test()\n");
10         this->i = 0;
11     }
12  
13     Test(int i)
14     {
15         printf("Test::Test(int i)\n");
16         this->i = i;
17     }
18  
19     Test(const Test& obj)
20     {
21         printf("Test(const Test& obj)\n");
22         this->i = obj.i;
23     }
24  
25     static void func()
26     {
27         printf("void Test::func()\n");
28     }
29  
30     void func(int i)
31     {
32         printf("void Test::func(int i), i = %d\n", i);
33     }
34  
35     int getI()
36     {
37         return i;
38     }
39 };
40  
41 void func()
42 {
43     printf("void func()\n");
44 }
45  
46 void func(int i)
47 {
48     printf("void func(int i), i = %d\n", i);
49 }
50  
51 int main()
52 {
53     func();
54     func(1);
55  
56     Test t;        // Test::Test()
57     Test t1(1);    // Test::Test(int i)
58     Test t2(t1);   // Test(const Test& obj)
59  
60     func();        // void func()
61     Test::func();  // void Test::func()
62  
63     func(2);       // void func(int i), i = 2;
64     t1.func(2);    // void Test::func(int i), i = 2
65     t1.func();     // void Test::func()
66  
67     return 0;
68 }
复制代码

九.运算符重载

譬如+ - * / %等算术运算符和> < == !=等关系运算符就是典型的可重载运算符(但不是所有的运算符都可以重载,譬如sizeof)。

表面上,运算符重载是对C++源生运算符的意义,在某个class中做重定义。

本质上,运算符被映射到执行相应的成员函数,所以运算符重载其实是重定义对象的运算符所对应的函数。

复制代码
 1 #include <iostream>
 2  
 3 using namespace std;
 4  
 5 class coordinate
 6 {
 7 public:
 8     int x;
 9     int y;
10     
11     coordinate();
12     coordinate(int x0,int y0);
13     void print(void);
14     
15     //定义类的时候,提供一个运算符重载的对应解析函数即可
16     //返回值    函数名       参数列表
17     coordinate operator+ (const coordinate other);        
18 };
19  
20 //默认构造函数
21 coordinate::coordinate()
22 {
23     x = 0;
24     y = 0;
25 }
26 //带参数构造函数
27 coordinate::coordinate(int x0,int y0)
28 {
29     x = x0;
30     y = y0;
31 }
32  
33 //打印坐标函数
34 void coordinate::print(void)
35 {
36     cout << "(" << this->x <<","<< this->y << ")" <<endl;
37 }
38  
39 //运算符重载"+"解析函数
40 coordinate coordinate::operator+ (const coordinate other)
41 {
42     //在该函数内,去实现"+"的真正应该做的操作
43     coordinate tmp;
44     tmp.x = this->x + other.x;
45     tmp.y = this->y + other.y;
46     
47     return tmp;
48 }
49  
50  
51 int main()
52 {
53     coordinate a(1,3);
54     coordinate b(2,5);
55     coordinate c;
56     
57     c = a+b;          //被编译器翻译成: c = a.operator+(b);
58     
59     c.print();        // 成功打印 (3,8)
60     
61     return 0;
62 }
复制代码

运算符+的重载:

复制代码
 1 coordinate coordinate::operator+ (const coordinate other)
 2 {
 3     //在该函数内,去实现"+"的真正应该做的操作
 4     coordinate tmp;
 5     tmp.x = this->x + other.x;
 6     tmp.y = this->y + other.y;
 7     
 8     return tmp;
 9 }
10  
复制代码

运算符=的重载:

复制代码
//运算符重载"="解析函数1_2
 
void coordinate::operator=(const coordinate other)
{
    //在该函数内,去实现"="的真正应该做的操作
    //c = a;  c是this,a是other,c=a整个表达式的值是返回值
    this->x = other.x;
    this->y = other.y;
    
    return ;       //实现为无返回值的函数时,在对对象进行 = 的时候不能连等,也就是说不支持 d = (c = a) ;
}
 
coordinate coordinate::operator=(const coordinate other)
{
    //在该函数内,去实现"="的真正应该做的操作
    //c = a;  c是this,a是other,c=a整个表达式的值是返回值
    this->x = other.x;
    this->y = other.y;
    
    return *this;     //实现为有返回值的函数时可以支持 d = (c = a) ;这样连等的形式
}
复制代码

运算符+=的重载:

复制代码
 1 //运算符重载"+="解析函数
 2 void coordinate::operator+=(const coordinate other)
 3 {
 4     //a += b;             效果上等价于 a = a + b;
 5     // c = (a += b);    我们一般不会这样写,所以定义成无返回值就行
 6     //a是this ,b是other ,操作完成a 的值改变,b的值不改变
 7     this->x = this->x + other.x;
 8     this->y = this->y + other.y;
 9     return;
10 }
复制代码

 

posted @   学习&笔记  阅读(373)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示