一.编译器会为我们做什么
当声明一个空类的时候,编译器会
根据需要生成:默认
构造函数,拷贝(copy)构造函数,赋值
caozuofu.html" target="_blank">操作符
重载函数(copy assignment),
析构函数.上面四个函数,当你声明一个类的时候,你没有定义的时候,编译器会根据需要帮你生成,
他们都是 public 的并且是 inline .注意:只有这些函数被调用的时候,编译器才会主动帮你生成.对于copy assignment编译器还会检查生成的代码是否合法,如果不合法,编译器拒绝生成.
对于copy构造函数和copy assignment操作符,编译器创建的
版本只是单纯的将来源对象的每一个 non-static成员变量拷贝到目标对象.这在很多情况下会引起
错误.
例如:
template<class T>
class NamedObject {
public:
NamedObject(std::string& name, const T& value);
... // 假设没有声明任何 operator=
private:
std::string& nameValue; // 现在是一个引用
const T objectValue; // 现在为 const 的
};
当如下使用的时候:
std::string newDog("Persephone");
std::string oldDog("Satch");
NamedObject<int> p(newDog, 2);
NamedObject<int> s(oldDog, 36);
p = s; // 对于 p 中的数据成员将会发生 // 什么呢?
注意p=s这句.它是什么含义呢?
1.p.nameValue应该指向s.nameValue所指向的那个String么?这显然是不符合C++语法规则的.C++不允许"引用改指不同的对象",引用一旦赋值就不能更改.
2.p.nameValue所指向的那个String对象应该改变么?如果改变那么会影响"持有pointer或者reference而且指向该String"的其他对象.
因为有很大的不确定性和影响,编译器将拒绝编译p=s这句话,而将决定权交给你.
你如果需要在一个内含有reference成员的类里支持赋值操作(assignment),你必须自己定义赋值操作符(copy assignmen).
面对内含"const 成员"的类,编译器的反应也是一样的.因为更改const成员是不合法的,编译器无法面对给const成员赋值的情况.
还有种情况:当基类(base classes)把赋值操作符(copy assignment)定义为private,编译器将拒绝为子类(derived classes)生成默认的copy assignment.因为编译器为子类创建的copy assignment会调用 base classes的 copy assignment(无
访问权限).
如果一个类不想被拷贝,即不支持copy构造函数,copy assinment.有两种方法:
1.自己声明一个对应的private方法,而不去实现它(防止被成员函数或者友元函数调用).这样谁调用它,将会引发一个link错误.
2.定义个类,copy构造函数和copy assignment定义为private.不想实现这一功能的类继承这个类.因为编译器生成的copy构造函数和copy assignment函数会去调用父类的对应函数,因权限问题,会引发编译错误.这样把link时期的错误转移到编译期间.
二.关于在构造函数,析构函数和虚函数
1.
多态的基类的析构函数必须为虚函数.
对工厂模式来说,工厂返回base class的指针,来指向子类对象.如果这个对象位于heap(对工厂模式来说,肯定是).我们要正确的delete掉它.
delete baseclasspointer;
如果baseclass的析构函数不是虚函数,这将会引发错误.因为它会调用base class的析构函数,子类对象部分将不会被释放!一个类被释放一部分,情况是不确定的!
如果一个类有虚函数,其每个对象将会维持一个虚函数表,这将导致对象体积的增大.会引起兼容,移植性的问题.
所以一个心得是:如果有一个virtual函数,才将其析构函数定义为virtual.
2.不要在构造函数和析构函数中使用虚函数!
对于构造函数:会先调用base class的的构造函数,此时对象是base class类型的,而不是derived class类型.base class 构造期间,虚函数不会下降到到derived class.这样你所期待的多态就落空了.
对于析构函数:会先析构derived class的成员,这样derived class的成员会呈现未定义的状态,C++视他们为不存在.进入base class的析构函数后,对象成为base class类型对象,而C++的虚函数,dynamic_casts(运行期类型信息)也这么看待它.
三.explicit 构造函数
这个主要是防止隐式转换所引起的错误.
隐式转换只会发生在构造函数只有一个参数的情况下.所以两个以上参数就不需要这个
关键字.
隐式转换的
例子:
class string
{
//...
public:
/**explicit*/ string(int size); // block implicit conversion
string(const char *); //implicit conversion
~string();
};
int main()
{
string s = "hello"; //OK, convert a C-string into a string object
int ns = 0;
s = 1;
}
上面的代码中
string s = "hello";
隐式转换为:
string temp("hello");
string s = temp;
s=1
这种代码,可能是你手贱将ns写成s,这也将编译通过.
还有种情况:
string s = "A"; //string(const char *)
string s1= 'A';//调用string(int size);
所以必须小心隐式转换!!
为什么不让构造函数默认为explicit呢?
很多旧的C++代码依赖隐式转换来实现.所以默认就是隐式转换.
一,二的内容来自于 Effective C++
三的内容来自于:ANSI/ISO C++ Professional Programmer's Handbook 和网络
ANSI/ISO C++ Professional Programmer's Handbook下载地址:
http://ishare.iask.sina.com.cn/f/20517525.html