【C++基础篇】学习C++就看这篇--->类和对象之static成员、友元、内部类、匿名对象
一、static成员背景如果我们要实现一个类这个类可以计算它总共实例化出多少个对象该咋实现呢相信以现阶段的知识而言肯定会说建立一个全局变量再类的构造函数和拷贝构造函数当中进行解释类要实例化出对象就必然要调用构造函数或者是拷贝构造函数如下所示代码语言javascriptAI代码解释int n 0; class A { public: A() { n; } A(const A t) { n; } private: };但这里会遇到一个问题如果其它地方把n改了呢这是不是违背了我们类的封装性的特性啊你n随便改能行所以static成员来了。✨1.1 概念声明为static的类成员称为类的静态成员用static修饰的成员变量称之为静态成员变量用static修饰的成员函数称之为静态成员函数。静态成员变量一定要在类外进行初始化如何用呢代码语言javascriptAI代码解释class A { public: A() { _scount; } A(const A t) { _scount; } static int GetACount() { return _scount; }//当然这个成员函数也可以被static修饰 //但是这样做了在函数体内就不能对非静态成员进行访问即没有this指针 private: static int _scount;//这里只是声明这样写_scount即属于这个类又属于每一个对象 }; int A::_scount 0;//这里才是定义 void TestA() { cout A::GetACount() endl;//当然如果定义成static成员想要获取就需要写一个成员函数 A a1, a2; A a3(a1); cout A::GetACount() endl; //如果将私有限定符改成公有的下面是都可以访问的 /*cout a1._secount endl; cout a2._secount endl; cout A::_secount endl;*/ } int main() { TestA(); return 0; }✨1.2 特性1️⃣静态成员为所有类对象所共享不属于某个具体的对象存放在静态区2️⃣静态成员变量必须在类外定义定义时不添加static关键字类中只是声明3️⃣ 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问4️⃣ 静态成员函数没有隐藏的this指针不能访问任何非静态成员5️⃣静态成员也是类的成员受public、protected、private 访问限定符的限制学了以上知识看两个小问题 1. 静态成员函数可以调用非静态成员函数吗 2. 非静态成员函数可以调用类的静态成员函数吗 答 1. 显然是不可以的静态成员函数是没有this指针的要调用非静态成员函数必须将对象地址传给非静态成员函数这一步是隐藏的 2. 显然是可以的无需解释。总结一下代码语言javascriptAI代码解释class A { public: static int GetACount() { return _scount; }//当然这个成员函数也可以被static修饰 private: static int _scount;//这里只是声明这样写_scount即属于这个类又属于每一个对象 }; int A::_scount 0;//这里才是定义 void TestA() { cout A::GetACount() endl;//当然如果定义成static成员想要获取就需要写一个成员函数 cout a1.GetACount() endl; }1. 如果想要访问私有static成员必须要写一个成员函数来获取2. 如果是static成员函数上述A::GetACount()和a1.GetACount()都可以使用但如果是普通成员函数就必须使用特定对象调用的方式a1.GetACount()二、友元友元提供了一种突破封装的方式有时提供了便利。但是友元会增加耦合度破坏了封装所以友元不宜多用。友元分为友元函数和友元类✨2.1 友元函数背景如上面Time类的几个成员变量是在私有限定符下这就导致了在类外面是不能访问的但是如果我必须要访问怎么办这就违背了限定符的作用那怎么办取个折中的方法你可以访问但是前提你必须是我的朋友所以友元函数就是解决上述问题的友元函数定义见下代码语言javascriptAI代码解释class Time { friend void f(Time t); private: int _hour 0; int _minute 0; int _second 0; }; void f(Time t) { t._hour 10; cout t._hour t._minute t._second endl; }有没有感觉莫名奇妙的韵味你既然要访问还弄这杂七杂八的干嘛直接定义成成员函数不就行了确实是这个样但是友元函数也确实有它的应用场景见下前面的学习过程中我们打印都是写一个print函数但是我们能不能像cout那样直接打印呢代码语言javascriptAI代码解释cout t;这样肯定是不行的就像前面学习的运算符重载那样直接用是不行的所以要想使用上面的这种形式就必须重载代码语言javascriptAI代码解释void operator(ostream out)//ostream是iostream中cout对象类型 { out _hour _minute _second endl; }但你会发现还是编不过原因就是讲述运算符重载时说过左操作数是重载函数的第一个参数即隐含的this指针右操作数是重载函数的显式形参但你现在所表达的是cout是左操作数t是右操作数因而不行代码语言javascriptAI代码解释t cout;//所以要用只能这种形式但这不符合常规啊可读性不好那怎么办this指针是隐含的而且我们知道它必须是第一个参数变不了因此友元函数的作用来了代码语言javascriptAI代码解释class Time { public: friend void operator(ostream out, const Time t1); Time(int hour 0, int minute 0, int second 0) :_hour(hour) , _minute(minute) ,_second(second) {} private: int _hour 0; int _minute 0; int _second 0; }; void operator(ostream out, const Time t1)//ostream是iostream中cout对象类型 { out t1._hour t1._minute t1._second endl; } int main() { Time t(20, 14, 50); //我们将重载函数定义到类外面 cout t; return 0; }补充知识 ostream是iostream中cout对象类型 istram是iostream中cin对象类型cout我们一般还有这样操作过代码语言javascriptAI代码解释cout i j endl;连续的输出但是我们上面所实现的并不能完成上面功能 需要如下做代码语言javascriptAI代码解释ostream operator(ostream out, const Time t1)//ostream是iostream中cout对象类型 { out t1._hour t1._minute t1._second endl; return out; }我们还有输入cin原理和上面类似连续输入的实现如下代码语言javascriptAI代码解释istream operator(istream in, Time t1)//istream是iostream中cin对象类型 { in t1._hour t1._minute t1._second ; return in; }注意友元函数可访问类的私有和保护成员但不是类的成员函数友元函数不能用const修饰友元函数可以在类定义的任何地方声明不受类访问限定符限制一个函数可以是多个类的友元函数友元函数的调用与普通函数的调用原理相同总结一下友元函数在 C 中的主要应用场景是当某个函数需要直接访问一个类的私有或保护成员但该函数逻辑上又不应该或不能是这个类的成员函数时。它打破了严格的封装边界但提供了必要的灵活性。