资讯详情

【C++入门】类和对象(下)——初始化列表、static成员、友元、内部类与匿名对象,一篇搞定!

📅 2026/10/7 14:15:08 | 华诺云谱 👁 阅读
【C++入门】类和对象(下)——初始化列表、static成员、友元、内部类与匿名对象,一篇搞定!
前言类和对象的最后一篇来了这篇我们学习初始化列表、隐式类型转换、static静态成员、友元、内部类、匿名对象以及编译器对对象拷贝的优化。这些知识点虽然零散但面试和写代码时经常遇到。 目录一、初始化列表二、隐式类型转换三、static静态成员四、友元五、内部类六、匿名对象七、对象拷贝的编译器优化一、初始化列表1.1 什么是初始化列表之前我们实现构造函数时主要在函数体内赋值。构造函数还有一种初始化方式——初始化列表// 以冒号开始每个成员变量后跟括号中的初始值逗号分隔classTime{public:Time(inthour):_hour(hour)// 初始化列表{coutTime()endl;}private:int_hour;};1.2 必须在初始化列表初始化的成员以下三种成员必须放在初始化列表位置否则编译报错引用成员变量const成员变量没有默认构造函数的类类型成员classDate{public:Date(intx,intyear1,intmonth1,intday1):_year(year),_month(month),_day(day),_t(12)// Time没有默认构造必须在初始化列表初始化,_ref(x)// 引用成员必须初始化,_n(1)// const成员必须初始化{}private:int_year;int_month;int_day;Time _t;// 没有默认构造int_ref;// 引用constint_n;// const};1.3 初始化顺序初始化列表中成员按照在类中声明的顺序初始化跟初始化列表中出现的先后顺序无关。建议声明顺序和初始化列表顺序保持一致避免出问题。来看一个经典面试题classA{public:A(inta):_a1(a),_a2(_a1)// 注意先声明的是_a2所以_a2先初始化{}voidPrint(){cout_a1 _a2endl;}private:int_a22;// 先声明_a2先初始化int_a12;};intmain(){Aaa(1);aa.Print();// 输出 1 随机值return0;}答案_a2先初始化用未初始化的_a1是随机值_a1后初始化为1。所以输出1 随机值。1.4 C11的缺省值C11支持在成员变量声明位置给缺省值这个缺省值是给没有显示在初始化列表初始化的成员使用的classDate{public:Date():_month(2){}private:int_year1;// 缺省值初始化列表没给就用1int_month1;// 初始化列表给了2就用2int_day;// 没有缺省值初始值随机};注意声明位置给的不是初始化是缺省值最终还是要走初始化列表。二、隐式类型转换2.1 内置类型转类类型C支持内置类型隐式类型转换为类类型对象前提是有相关内置类型为参数的构造函数classA{public:A(inta1):_a1(a1){}private:int_a1;};intmain(){A a11;// 1先构造一个A的临时对象再用临时对象拷贝构造a1// 编译器遇到连续构造拷贝构造会优化为直接构造return0;}2.2 explicit关键字构造函数前面加explicit就不再支持隐式类型转换classA{public:explicitA(inta1):_a1(a1){}private:int_a1;};intmain(){// A a1 1; // 编译报错explicit禁止了隐式转换Aa1(1);// 这样可以显式构造return0;}2.3 类类型之间的转换类类型对象之间也可以隐式转换需要相应的构造函数支持classA{public:A(inta11,inta22):_a1(a1),_a2(a2){}intGet()const{return_a1_a2;}private:int_a1;int_a2;};classB{public:B(constAa):_b(a.Get()){}private:int_b;};intmain(){A aa3{2,2};// C11支持多参数隐式转换B baa3;// A对象隐式转换为B对象return0;}三、static静态成员3.1 静态成员变量用static修饰的成员变量称为静态成员变量静态成员变量一定要在类外进行初始化为所有类对象所共享不属于某个具体对象不存在对象中存放在静态区classA{private:staticint_scount;// 类里面声明};intA::_scount0;// 类外面初始化3.2 静态成员函数用static修饰的成员函数称为静态成员函数静态成员函数没有this指针只能访问静态成员不能访问非静态成员因为没有this非静态成员函数可以访问静态成员3.3 访问方式突破类域就可以访问静态成员有两种方式// 1. 类名::静态成员coutA::GetCount()endl;// 2. 对象.静态成员couta1.GetCount()endl;静态成员也是类的成员受public、protected、private访问限定符限制。3.4 经典例子计算创建了多少个对象classA{public:A(){_scount;}A(constAt){_scount;}~A(){--_scount;}staticintGetCount(){return_scount;}private:staticint_scount;};intA::_scount0;intmain(){coutA::GetCount()endl;// 0A a1,a2;Aa3(a1);coutA::GetCount()endl;// 3return0;}四、友元4.1 友元是什么友元提供了一种突破类访问限定符封装的方式分为友元函数友元类使用friend关键字把友元声明放到类里面。4.2 友元函数外部友元函数可以访问类的私有和保护成员。友元函数只是一种声明不是类的成员函数。classA{friendvoidfunc(constAaa);// 友元声明private:int_a11;};voidfunc(constAaa){coutaa._a1endl;// 可以访问私有成员}4.3 友元类classA{friendclassB;// B是A的友元类private:int_a11;};classB{public:voidfunc1(constAaa){coutaa._a1endl;// B的成员函数可以访问A的私有成员}};4.4 友元的特性友元函数可以在类定义的任何地方声明不受访问限定符限制一个函数可以是多个类的友元函数友元关系是单向的A是B的友元不代表B是A的友元友元关系不能传递A是B的友元B是C的友元A不是C的友元友元增加了耦合度破坏了封装不宜多用五、内部类5.1 什么是内部类如果一个类定义在另一个类内部这个类就叫做内部类。内部类是一个独立的类外部类定义的对象中不包含内部类内部类默认是外部类的友元类内部类本质也是一种封装classA{private:staticint_k;int_h1;public:classB// 内部类默认是A的友元{public:voidfoo(constAa){cout_kendl;// OKcouta._hendl;// OK友元可以访问}private:int_b1;};};intA::_k1;intmain(){coutsizeof(A)endl;// 内部类不占外部类对象空间A::B b;// 内部类要通过外部类类域访问A aa;b.foo(aa);return0;}5.2 内部类的使用场景当A类跟B类紧密关联A类实现出来主要就是给B类使用那么可以考虑把A类设计为B的内部类。如果放到private/protected位置A类就是B类的专属内部类其他地方都用不了。六、匿名对象6.1 什么是匿名对象用类型(实参)定义出来的对象叫做匿名对象不取名字classA{public:A(inta0){coutA(int a)endl;}~A(){cout~A()endl;}};intmain(){A aa1;// 有名对象A();// 匿名对象A(1);// 匿名对象return0;}6.2 匿名对象的特点匿名对象生命周期只在当前一行下一行就自动调用析构函数一般临时定义一个对象当前用一下即可就可以定义匿名对象classSolution{public:intSum_Solution(intn){returnn;}};intmain(){// 匿名对象场景临时调用一下函数Solution().Sum_Solution(10);return0;}七、对象拷贝的编译器优化7.1 为什么要优化现代编译器为了尽可能提高程序效率在不影响正确性的情况下会尽可能减少一些传参和传返回值过程中可以省略的拷贝。7.2 常见优化场景场景1传值传参voidf1(A aa){}intmain(){A aa1;f1(aa1);// 构造拷贝构造f1(1);// 隐式类型转换连续构造拷贝构造 - 优化为直接构造f1(A(2));// 一个表达式中连续构造拷贝构造 - 优化为一个构造return0;}场景2传值返回Af2(){A aa;returnaa;}intmain(){f2();// 传值返回不优化会产生临时对象A aa2f2();// 连续拷贝构造拷贝构造 - 优化为一个拷贝构造aa1f2();// 一些编译器会进行跨行优化return0;}7.3 关闭优化linux下用g编译时可以用以下方式关闭构造相关的优化g test.cpp -fno-elide-constructors具体优化C标准没有严格规定不同编译器处理方式不同。VS2022比VS2019优化得更激进会进行跨表达式合并优化。类和对象的内容到这里就全部结束了从类的定义、构造析构、拷贝构造、运算符重载到初始化列表、static成员、友元、内部类这些都是C面向对象的基础。后面学习STL时会大量用到这些知识加油
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑