目录
一.static成员
测试:
实现一个类,计算程序中创建出了多少个类对象。
namespace lj { int count = 0; } class A { public: A() { ++lj::count; } A(const A& t) { ++lj::count; } ~A() { } private: }; A func() { A aa;//构造 return aa;//拷贝构造 } int main() { A aa;//构造 func(); cout << lj::count << endl;//3 return 0; }
虽然count准确计算出了A类一共创建了3个对象,但由于全局的特性使其无法再接着计数其他类的对象。
我们尝试不让它作为全局变量,而是放在类里~
这样做会使得每一个创建的对象都有自己的count,无法进行计数叠加。
所以下面引出了static成员进行辅助~
1. 静态成员 为 所有类对象所共享 ,不属于某个具体的对象,存放在静态区2. 静态成员变量 必须在 类外定义 ,定义时不添加 static 关键字,类中只是声明3. 类静态成员即可用 类名 :: 静态成员 或者 对象 . 静态成员 来访问4. 静态成员函数 没有 隐藏的 this 指针 ,不能访问任何非静态成员5. 静态成员也是类的成员,受 public 、 protected 、 private 访问限定符的限制有两点需要注意:在类内由static修饰的变量不能带有缺省值,因为缺省值是给初始化列表准备的,而初始化列表是去初始化某个对象。但此时被修饰后的count不再是属于每个对象了,而是属于所属类~
因此只能用类内声明,类外定义来初始化。
若我们让count公有化,则可以正常使用count计数,无论是指定域还是指定对象都可以调用
若继续让count私有化,我们就得借助成员函数来帮我们调用了(成员函数是公有的)。
不过这样的成员函数只能靠对象才能调用,无法通过指定域调用。
如果没有aa对象还有两种方案:
要么临时创立一个对象去调用成员函数,不过这样最后的count要减1,因为aa1是我们为了调用而被迫创建的。
要么使用匿名对象——生命周期只有这一行,优点只比有名对象(aa1)节省一行,当然同样要count-1。
不过这些都太麻烦,这也引出了下一个知识点:静态成员函数
静态成员函数中无论是指定域还是指定对象都可以去访问。
普通成员函数一般是通过对象来找到所属域最后访问count,也可以是传递对象地址通过隐藏this指针找到函数中的count。
不过静态成员函数因为没有隐藏this指针因此不能访问函数中的非静态成员变量。
就算是计算类中大小也不会把静态成员变量计入,因为不在对象内,而是静态区中。
总结:使用静态成员变量与静态成员函数会有全局的效果,只不过会被类域和访问限定符限制。
二.explicit关键字
构造函数不仅可以构造与初始化对象, 对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用 。class A { public: A(int a) :_a(a) { } private: int _a = 0; }; int main() { A aa1(1); A aa2(2); A aa3 = 3; }
aa1与aa2都可以构造成功,那么aa3呢?
.
内置类型隐式类型转换,所以aa3最后也可以成功构造出来。恰好3为Int类型与构造函数中的形参一致才可以构造成功,若换其他的类型(例如int*)最终会因为没有匹配的构造函数导致对象创建失败。
当然这是没有用explicit关键字 的效果:当我们使用后发现它会让隐式类型转换失效,但它无法限制强制转换。
我们再来尝试一下多参的隐式类型转换
只有d1与d3是符合要求的,而d2那个是逗号表达式,因此初始化不合理。
三.友元(少用)
友元函数:
如果一个全局函数想要访问类里面的私有成员,可以在类内放友元声明。例如我们前面学过的流插入,流提取运算符。
友元类:
即一个类可以去访问另一个类的私有成员~
class Time { friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类 中的私有成员变量 public: Time(int hour = 0, int minute = 0, int second = 0) : _hour(hour) , _minute(minute) , _second(second) {} private: int _hour; int _minute; int _second; }; class Date { public: Date(int year = 1900, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} void SetTimeOfDate(int hour, int minute, int second) { // 直接访问时间类私有的成员变量 _t._hour = hour; _t._minute = minute; _t._second = second; } private: int _year; int _month; int _day; Time _t; };
四.内部类(少用)
顾名思义,就是在类的内部再定义一个类。
特性:1. 内部类可以定义在外部类的public、protected、private都是可以的。2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。3. sizeof(外部类)=外部类,和内部类没有任何关系。class A { private: static int k; int h; public: class B // B天生就是A的友元 { public: void foo(const A& a) { cout << k << endl;//OK cout << a.h << endl;//OK } }; }; int A::k = 1; int main() { A::B b; b.foo(A()); return 0; }
若B类定义为私有,那么类外的A::bb就无法构建了,只能在类内实例化。
五.(扩展)编译器的优化
赋值拷贝的前提是已存在的两个对象进行赋值。
而拷贝构造是一个已存在对象去初始化另一个要创建的对象。所以aa3其实是拷贝构造~
下面则是编译器的优化规则:
熟悉这些规则只是帮助我们更好理解编译完成所显现的信息,让我们了解为什么有的构造信息会被省略,原因就在于编译器自带的优化~