一、static
静态成员属性为什么要在类外初始化?
静态成员属性之所以不能在构造函数内初始化,是因为构造函数必须要在定义对象的时候才会调用,而static变量在编译的时候就创建了,所以要在类外通过类名作用域对static成员属性初始化。
注意:sizeof()在C++中和C语言有不同,当我们:
cout << sizeof(//类名) << endl;
的时候,不是输出这个类的大小,而是输出定义一个这个类的对象需要申请多大的空间,所以如果这个类里包含了静态成员都不会计算在sizeof()内的,因为静态成员一个类只有一份儿,所以的对象共用他们。
struct Node
{
int age;
char c;
};
class CPeople
{
public:
int pp;
static const int qq = 100; //静态常量整形数据成员才能在类内初始化
static Node node[2];
static int num; // 静态成员一定要在类外初始化
public: //相当于全局变量,在编译的时候就申请空间了,不用对象也能调用,(类名::)
CPeople()
{
pp = 100;
}
// 静态成员函数:不能用普通的成员属性,只能用 static 成员,因为没有this指针
static void Show()
{
//cout << this->pp << endl;
cout << num << endl;
cout << "static::show" << endl;
}
};
Node CPeople::node[2] = {{123,'d'},{456,'y'}}; //静态结构体数组初始化
int CPeople::num = 123; //静态成员的初始化
注意:静态常量整形数据成员才能在类内初始化。所以short、long、char都可以,但是float、double不可以。
二、const
非静态const成员属性为什么一定要在初始化列表里进行初始化?
在C中我们知道#define进行预定义的某个数是被分配内存的,其文件在编译预处理过程中就会用定义好的数据去替代文中的符号。但是const却是不一样的,一般情况下编译器也是不为const创建空间的,只是将这个定义的数字保存在符号表中的。所以C++中const是在编译的时候定义的,但是它的初始化可以在调用构造函数(也就是创建对象)的时候通过初始化列表的方式进行。调用构造函数的时候,首先是给该实例(类的具体对象)分配空间。使用初始化列表的话,就在分配空间的时候,同时将其空间初始化。但在构造函数的大括号里,所有变(常)量的空间都已经分配好了,进行赋值操作。我们知道const是常量,只能进行初始化操作而不能进行赋值操作,所以必须在初始化列表中分配空间的同时进行初始化操作。
还有一个赋值次数,效率上的区别,初始化列表在对象初始化时对成员变量赋值一次,而使用构造函数内直接赋值的话,对成员变量赋值两次,一次是对象构造是用默认值进行赋值,第二次是调用构造函数赋值。因为普通成员变量是否初始化都可以,而且允许赋值操作,所以普通成员变量既可以使用初始化列表又可以使用构造函数赋值。
参考:http://blog.youkuaiyun.com/zhouyelihua/article/details/23252667
const成员函数
void Show(/*const CPeople* this*/) const // 常函数,不能修改类中的成员属性
{
//this->a = 100; //错误,因为 this 已经变成 const CPeople* this
}
常量类型的对象:
const CPeople pep;
只能调用常函数,普通函数用不了,因为我们知道成员函数在使用的时候要把对象的地址传进去,而我们用常量对象调用普通成员变量的时候,相当于:
void AA(/*CPeople* this = const CPeople* ll*/)
{
a = 567;
}
一定会报错的。而const成员函数的this已经变了,所以常量对象可以调用const成员函数。反过来普通对象可以调用常函数。
三、friend
friend class BB; // 友元类
friend void Show(CPeople pp); // 友元函数,可以让这个东西用自己的private成员
无论是友元类还是被友元类,都不能继承这个友元关系。友元的友元也不能使用。友元就是只限定这一个可以用。
类的封装性问题
友元函数是破坏类的封装性的最大杀手。我们在使用成员函数返回类的私有成员属性的时候也是很危险的,如果一定要使用的话,就将返回值设为const。
四、inline
void AA() // 类内实现的都是内联函数(默认的)
{
cout << "AA" << endl;
}
/*inline */void BB(); // 不是内联的
inline void BB(); // 内联的
不过编译器自己也会有判断,其实声明为inline只是对编译器提出一个建议,建议编译器进行内联,而具体会不会内联,由编译器决定,编译器会综合考虑更方面因素来判断要不要进行内联。
内联将函数代码直接在调用的地方展开,而函数调用是一个跳转语句,通过函数指针跳转到函数体的地址,执行完再回来,所以inline是一种用空间换时间的方法。
参考我的另一篇博客:http://blog.youkuaiyun.com/u012300157/article/details/46763089