1.构造函数和复制构造函数相关的初始化
最近由于项目比较闲,自己自学了一些C++的知识,也在博客上总结一下。
首先理解“初始化”和“赋值”,一个新对象创建时,才会有初始化操作,初始化操作出现在构造函数中;而赋值是用来修改一个已经存在的对象的值,出现在operator=操作函数中。
1.1复制构造函数
class Complex
{
public:
Complex(double, double);//普通构造函数
Complex(const Complex&);//复制构造函数
};
如果我们没有在类中声明一个复制构造函数,编译器也会为我们合成一个,这个缺省的复制构造函数会将新对象中的每个数据成员初始化为源对象中相应成员的值。
1.1.1缺省的复制构造函数的行为是否符合我们的会符合我们的要求
普通的:)
class Complex
{
private:
double real_d;
double imag_d;
public:
Complex(double r, double i):real_d(r), imag_d(i){}
//....
};
如上这种没有明确声明的复制构造函数,它的行为就是复制两个数据成员,这符合我们预期的操作,是ok的
(特别的:)
class String
{
private:
char* data;
public:
String(const char* cp = "");
~String() {delete [] data;}
};
String::String(const char* cp):data(new char[strlen(cp)+1])
{
strcpy(data, cp);
}
如上这种缺省的复制构造函数就不符合我们的预期了,应为如果有多个String对象,它会指向同一块内存,如果释放一个String对象,其它的也会被释放所以我们必须明确的声明复制构造函数:
class String
{
private:
char* data;
public:
String(const char* cp = "");
String(const String&);
~String() {delete [] data;}
};
String::String(const String& s):data(new char[strlen(s.data)+1])
{
strcpy(data, s.data);
}
这样就能确保每个String都会拥有一份数据的私有拷贝
对于缺省的复制构造函数是否能够正常工作,并没有一个通用的规则。从经验中得到的方法就是:对于包含指针的类要特别注意,如果被指向的对象是“属于”该类产生的对象,那么缺省的复制构造函数就有可能是错误的,因为它只是简单的复制了指针,而不是指针所指向的对象。复制构造函数在编码过程中是不可被忽略的!
1.2类成员的初始化
当类中的某个数据成员本身也是一个类对象时,我们应该避免用赋值操作来为该成员进行初始化,如下:
class Employee
{
private:
String name;
public:
Employee(const String&);
};
Employee::Employee(const String& n)
{
name = n;//效率不高
}
这样做虽能得到正确结果,但效率并不高,因为一个新Employee对象被创建时,成员name先将会被String的缺省构造函数所初始化,而在Employee的构造函数中又会被初始化一次,所以这样做效率不高。我们可以把它合并成一步如下:
class String
{
public:
String();
String(const String&);
};
class Employee
{
private:
String name;
public:
Employee(const String&);
};
Employee::Employee(const String& n):name(n){}
我们用String::String(n),这样编译器就只会用一次函数调用来初始化namne这个成员,在效率上会有30%的提升。同样,那些内建类型的成员也可以用这种初始化列表的方式来进行初始化,这样做会使代码的可读性更高。
1.3成员初始化的顺序
C++中规定:一个类中的成员的初始化顺序和它在类中被声明的顺序(而不是构造函数定义的顺序)必须是一致的。忽略这种顺序在一些情况下不会有影响,但在某些场合下,就会导致问题,比如:某个成员的初始化过程中使用了另外成员的值,那么“另外成员”的初始化就必须在“某个成员”的前面了。一般我们最好在头文件把这种顺序记录下来,这样会给我们带来便利。
1.4类成员的引用
如果在类中某个非静态数据成员是一个引用的话,因为所有的引用都必须被明确的初始化,所以我们必须在该类的每个构造函数中都使用初始化语法:
Class Payroll_entry
{
Private:
Employee& emp;
Public:
Payroll_entry(Employee&);
};
Payroll_entry::Payroll_entry(Employee& e){}//编译错误:“emp”必须被初始化
所以我们在设计类的时候,为什么要将emp声明为Employee&,而不是Employee*呢?因为声明为引用会有下面两方面的限制:
1.我们必须在创建emp的时候就必须初始化
2.一旦绑定初始化后,emp就能不改变