一、C++提供的缺省操作
如果一个类不提供某些操作,C++编译器会提供缺省/默认的相关操作,而且这些操作都是public的。
1.缺省构造:如果你没有定义任何构造函数,编译器将在你定义个该类的对象的时候为你提供一个缺省构造函数,分别调用该类的成员变量对应的缺省构造函数对该类的各个成员进行初始化。
class defualtServer
{
int m_i;
string m_str;
};
defualtServer ds;
如上代码,当你调用defualtServer定义个对象时,C++编译器为你提供一个缺省构造函数,此构造函数被调用后 m_i的值是不确定的,而m_str却是个空字符串。因为int没有构造函数其值是不确定的,string有缺省构造函数,构造一个空字符串。
2.缺省拷贝构造(缺省赋值运算):缺省拷贝构造函数(赋值运算符)对类的非静态数据成员进行 “以成员为单位的” 逐一拷贝构造(赋值)。看以下例子,你就明白你为何要为含有动态内存分配的类提供一个拷贝构造函数(赋值操作符)而不是使用缺省的了:
class stringWithNoCopyConstruct {
private:
char * m_str;
public:
const char *get_str() {return m_str;}
stringWithNoCopyConstruct(char *str){
if (str)
{
m_str = new char[strlen(str) + 1];
strcpy(m_str, str);
}
else
{
m_str = new char[1];
m_str[0] = '\0';
}
}
void set_str(char *str) {
if (str)
{
delete[] m_str;
m_str = new char[strlen(str) + 1];
strcpy(m_str, str);
}
else
{
m_str = new char[1];
m_str[0] = '\0';
}
}
~stringWithNoCopyConstruct() { delete[] m_str; }
};
int main()
{
stringWithNoCopyConstruct dsp1("first construct");
stringWithNoCopyConstruct dsp2(dsp1);
dsp1.set_str("set str");
cout << "dsp2.m_str = " << dsp2.get_str() << endl;//输出的是乱码而不是"first construct"
return 0;
//return之后要调用析构函数了,先析构dsp2再析构dsp1。但是在析构dsp2的时候就因为double-free造成程序crash了
}
分析:
程序为什么会double-free呢?原因在于dsp2(dsp1)调用的时候并没有为dsp2的m_str内存,而是只是简单的将dsp1的m_str的值拷贝给了dsp2的m_str,造成两个char *指向了同一个内存。因此也发生了上述的当重写dsp1的值时,dsp2指向的内存被释放而变成乱码。当dsp2析构的时候,再次析构上次dsp1已经释放了的内存造成典型的double-free。
解决之道:为类 stringWithNoCopyConstruct 提供显示的拷贝构造函数。
stringWithNoCopyConstruct(const stringWithNoCopyConstruct & sc) {
//stringWithNoCopyConstruct(sc.m_str);
if (sc.m_str)
{
m_str = new char[strlen(sc.m_str) + 1];
strcpy(m_str, sc.m_str);
}
else
{
m_str = new char[1];
m_str[0] = '\0';
}
}
问题:为啥调用构造函数(上面注释的部分)会代替上面代码会造成stringWithNoCopyConstruct.m_str构造完了之后变成NULL???百思不得其解!!
3.缺省析构:缺省析构函数一般都是非虚析构,除非其基类的析构函数是虚函数。而这个析构函数啥也不做。(为什么隐式生成的拷贝构造函数和赋值运算符要象现在这样工作呢,尤其是指针?因为这是C对struct进行拷贝和赋值的方式,和C兼容很重要。)
4.缺省取地址运算:仅仅是返回this指针
二、确保非局部静态成员对象在使用前被初始化
如果多个类分隔在多个文件中,且某个全局的类对象依赖另外一个全局类的对象时,可能由于对象初始化顺序的不确定性导致无法达到预期的目的。此时可以使用单例模式来解决该问题。如:
classA &creatA(args...){
static classA sa;//初始化A
return sa;
}
这样做的好处是,只有第一次调用creatA时,sa才会被构造,保证了初始化顺序。
三、提高对C++的认识
参考书:The Annotated C++ Reference Manual
四、熟悉保准库