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 public: 19 // 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 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异