C++进阶教程:类与对象深入 本文是 C 系列教程的第 6 篇。上一篇初探了类的基本语法本篇深入类与对象构造函数与析构顺序、初始化列表、拷贝构造与拷贝赋值、深拷贝与浅拷贝、static 成员、const 成员函数、this 指针与友元。一、构造函数深入1.1 构造函数的种类#includeiostreamusingnamespacestd;classPerson{private:string name;intage;public:// 1. 默认构造无参Person():name(未命名),age(0){cout默认构造endl;}// 2. 带参构造Person(string n,inta):name(n),age(a){cout带参构造: nameendl;}// 3. 拷贝构造重要Person(constPersonother):name(other.name),age(other.age){cout拷贝构造: 复制 other.nameendl;}voidshow()const{coutname age岁endl;}};intmain(){Person p1;// 默认构造Personp2(张三,25);// 带参构造Person p3p2;// 拷贝构造Personp4(p2);// 拷贝构造等价写法p1.show();p2.show();p3.show();return0;}1.2 初始化列表推荐#includeiostreamusingnamespacestd;classStudent{private:constintid;// const 成员必须在初始化列表初始化string name;public:// 初始化列表语法在 : 后按声明顺序初始化Student(inti,string n):id(i),name(n){// 函数体可为空}voidshow()const{cout学号 id nameendl;}};intmain(){Students(1001,李四);s.show();// 学号 1001 李四return0;}初始化列表 vs 赋值const 成员和引用成员只能在初始化列表初始化初始化列表更高效直接构造而非先默认构造再赋值。1.3 构造与析枔的顺序#includeiostreamusingnamespacestd;classA{public:A(){coutA 构造endl;}~A(){coutA 析构endl;}};classB:publicA{// 继承 A第 8 篇详解public:B(){coutB 构造endl;}~B(){coutB 析构endl;}};intmain(){cout创建 B 对象:endl;B b;// 基类构造再派生类构造cout销毁 B 对象:endl;// 先派生类析构再基类析构return0;}输出顺序创建 B 对象: A 构造 B 构造 销毁 B 对象: B 析构 A 析构规则构造顺序 基类 → 成员 → 自身析构顺序完全相反。二、深拷贝与浅拷贝2.1 浅拷贝的灾难悬垂指针#includeiostream#includecstringusingnamespacestd;classStringBad{private:char*str;// 动态分配的内存public:StringBad(constchar*s){strnewchar[strlen(s)1];strcpy(str,s);}// 默认拷贝构造逐字节复制浅拷贝// 两个对象共享同一块内存~StringBad(){delete[]str;// 两个对象都 delete 同一内存 → 双重释放崩溃}voidshow()const{coutstrendl;}};intmain(){StringBads1(Hello);StringBad s2s1;// 浅拷贝s1.str 和 s2.str 指向同一内存// 程序结束s2 先析构 delete[]s1 再析构 delete[] 同一地址 → 崩溃cout程序将在此后崩溃浅拷贝问题endl;return0;}2.2 深拷贝正确做法#includeiostream#includecstringusingnamespacestd;classString{private:char*str;public:// 构造String(constchar*s){strnewchar[strlen(s)1];strcpy(str,s);}// 拷贝构造深拷贝分配新内存复制内容String(constStringother){strnewchar[strlen(other.str)1];strcpy(str,other.str);cout深拷贝构造endl;}// 拷贝赋值运算符深拷贝Stringoperator(constStringother){if(thisother)return*this;// 自赋值检查delete[]str;// 释放旧内存strnewchar[strlen(other.str)1];strcpy(str,other.str);cout深拷贝赋值endl;return*this;}// 析构~String(){delete[]str;}voidshow()const{coutstrendl;}};intmain(){Strings1(Hello);String s2s1;// 深拷贝构造各自独立内存Strings3(World);s2s3;// 深拷贝赋值s1.show();// Hellos2.show();// Worlds3.show();// World// 各自独立析构无崩溃cout深拷贝程序正常结束endl;return0;}2.3 三法则Rule of Three如果类需要自定义析构函数则几乎总需要自定义拷贝构造函数拷贝赋值运算符因为默认实现都是浅拷贝与自定义析构释放资源矛盾。三、static 成员3.1 静态成员变量#includeiostreamusingnamespacestd;classCounter{private:staticintcount;// 静态成员所有对象共享public:Counter(){count;}~Counter(){count--;}staticintgetCount(){returncount;}// 静态成员函数};// 静态成员必须在类外定义分配存储intCounter::count0;intmain(){cout初始: Counter::getCount()endl;// 0Counter c1;Counter c2;{Counter c3;cout现在: Counter::getCount()endl;// 3}// c3 析构cout之后: Counter::getCount()endl;// 2return0;}3.2 静态成员函数特性#includeiostreamusingnamespacestd;classMathUtils{public:// 静态成员函数不依赖对象无 this 指针staticintmax(inta,intb){returnab?a:b;}staticintsquare(intx){returnx*x;}};intmain(){// 通过类名直接调用coutMathUtils::max(3,7)endl;// 7coutMathUtils::square(5)endl;// 25return0;}注意静态成员函数不能访问非静态成员没有 this 指针。四、const 成员函数#includeiostream#includestringusingnamespacestd;classAccount{private:string owner;doublebalance;public:Account(string n,doubleb):owner(n),balance(b){}// const 成员函数承诺不修改对象状态voidshow()const{coutowner 余额 balanceendl;// balance 0; // 错误const 成员函数不能修改}// 非 const 成员函数可修改voiddeposit(doubleamount){balanceamount;}// 可以同时有 const 和非 const 版本重载voidinfo()const{cout[只读] ownerendl;}voidinfo(){cout[可写] ownerendl;}};voidprintAccount(constAccountacc){// 参数是 const 引用只能调用 const 成员函数acc.show();// acc.deposit(100); // 错误}intmain(){Accounta(小明,100);printAccount(a);// const 引用调用a.deposit(50);// 非 const 对象可调用非 const 方法a.show();return0;}五、this 指针#includeiostream#includestringusingnamespacestd;classEmployee{private:string name;public:Employee(string name){// this 指针指向当前对象的指针this-namename;// 解决参数与成员同名}// 返回自身引用链式调用EmployeesetName(string name){this-namename;return*this;// 返回解引用的 this}voidshow()const{coutthis-nameendl;// this- 可省略}};intmain(){Employeee(张三);e.show();// 链式调用e.setName(李四).setName(王五);e.show();// 王五return0;}六、友元6.1 友元函数#includeiostreamusingnamespacestd;classBox{private:doublewidth;doubleheight;public:Box(doublew,doubleh):width(w),height(h){}// 声明友元函数可以访问私有成员frienddoublegetArea(constBoxb);friendBoxoperator(constBoxa,constBoxb);};// 友元函数实现不是成员函数doublegetArea(constBoxb){returnb.width*b.height;// 访问私有成员}Boxoperator(constBoxa,constBoxb){returnBox(a.widthb.width,a.heightb.height);}intmain(){Boxb1(3,4),b2(2,5);coutb1 面积: getArea(b1)endl;// 12Box sumb1b2;cout合并面积: getArea(sum)endl;// 45return0;}6.2 友元类#includeiostreamusingnamespacestd;classSecret{private:intcode;public:Secret(intc):code(c){}friendclassDecoder;// Decoder 可以访问 Secret 私有成员};classDecoder{public:voidreveal(constSecrets){cout解密密码: s.codeendl;// 访问私有}};intmain(){Secrets(9527);Decoder d;d.reveal(s);// 解密密码: 9527return0;}使用原则友元破坏封装应谨慎使用通常只在运算符重载等必要场景。七、实战完整 String 类综合本篇知识实现完整的动态字符串类#includeiostream#includecstringusingnamespacestd;classMyString{private:char*data;intlength;staticintcount;// 统计创建了多少个public:// 构造MyString(constchar*s){lengthstrlen(s);datanewchar[length1];strcpy(data,s);count;cout构造: dataendl;}// 深拷贝构造MyString(constMyStringother){lengthother.length;datanewchar[length1];strcpy(data,other.data);count;cout拷贝: dataendl;}// 深拷贝赋值MyStringoperator(constMyStringother){if(thisother)return*this;delete[]data;lengthother.length;datanewchar[length1];strcpy(data,other.data);cout赋值: dataendl;return*this;}// 拼接运算符重载MyStringoperator(constMyStringother)const{char*tempnewchar[lengthother.length1];strcpy(temp,data);strcat(temp,other.data);MyStringresult(temp);delete[]temp;returnresult;}// 比较运算符booloperator(constMyStringother)const{returnstrcmp(data,other.data)0;}// 析构~MyString(){cout析构: dataendl;delete[]data;count--;}voidshow()const{coutdataendl;}staticintgetCount(){returncount;}};intMyString::count0;intmain(){MyStrings1(Hello);MyStrings2( C);MyString s3s1;// 拷贝构造MyString s4s1s2;// 拼接s3s4;// 拷贝赋值s4.show();// Hello Ccout(s1MyString(Hello))endl;// 1cout存活对象数: MyString::getCount()endl;return0;}总结本篇深入讲解了构造函数的种类与初始化列表、构造析构顺序、深拷贝与浅拷贝的区别及三法则、static 成员、const 成员函数、this 指针和友元并用完整 String 类串联实战。重点掌握深拷贝防止双重释放、初始化列表的用法、static 与 const 的语法、this 指针的作用。下一篇将讲解运算符重载与类型转换敬请期待