默认构造函数的新认知
时间:2010-10-30 来源:wykitty
最近C++刚自学到运算符重载,在做一个String类的实验时,出现了很多让我觉得很诡异的问题,两个学长也帮我解决这些问题,发现问题是出在构造函数这里。纠结了两天的所谓诡异的问题,终于在今天被解决咯。。程序里出现的所有问题也被我搞清楚了。通过这次的问题程序,我对构造函数,尤其是默认构造函数有了更深一步的认识和了解,以下结合我的问题程序小小总结一下,平时不很受关注的默认构造函数。
默认构造函数(default constructor)---无参数
1.定义:在未提供显式的初始化值时,被用来创建对象的构造函数;
注:系统默认的构造函数只创建对象,并不做任何初始化工作。
e.g: String ob2;
若没有提供任何构造函数,则C++将自动提供默认构造函数,即系统默认构造函数。形式类似于这样:
String:: String ( ) { } ---> 不做任何工作;
注:
当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。所以如果已经为类定义了构造函数,您就必须为它提供默认构造函数。若提供了非默认构造函数(即普通构造函数),但没有提供默认构造函数。则当您声明: String ob2;时编译会出错。
String.cpp: In function ‘int main()’:
String.cpp:95: error: no matching function for call to ‘String::String()’
String.cpp:37: note: candidates are: String::String(const String&)
//这是我定义的复制构造函数
String.cpp:29: note: String::String(const char*)
//这是我的普通构造函数
提示没有找到默认构造函数匹配的格式,并给出了你自己定义的普通函数的格式,提示只有这两个候选构造函数,而没有默认构造函数,那么您就不能只main函数中像这样String ob2;只创建对象了。
2.定义默认构造函数的两种方式:
1)给已有的构造函数的所有参数提供默认值:
e.g:
Complex (float r=0, float i=0); //在类体中函数声明的格式
在类体外定义是还应是Complex ( float r, float i) { … }
注:
当类对象成员有指针变量时请注意您构造函数中的判断,后面马上说到。
在我的String类中用过这样的定义方式
String (const char *str = NULL);
类是这样定义的:
class String
{
public:
// String ( ); //先把自定义的默认构造函数隐掉,因为两种方式不能同时出现
String ( const char *str=NULL ); //在此声明
String ( const String &other );
~String ( );
String & operator = ( char *str );
String & operator = ( const String &other );
int operator == ( String &other );
int operator == ( char *str );
void print ( );
private:
char *m_data; //类中有指针变量型的对象成员
int length;
};
String :: String ( const char *str ) //普通构造函数
{
length = strlen ( str );
m_data = new char [length + 1];
strcpy ( m_data, str );
}
然后我在进行编译时,是通过的,然而在运行时却出现了段错误。也就是说如果按以上这个方式定义时,类中的指针变量指向的是非法空间;所以请注意,当您的类中定义了指针变量,那么在您的普通构造函数里,就需要做一个判断if (str == NULL)然后给m_data开辟一个空间去存放'\0';
也就是说您的构造函数应该这样写:
String :: String ( const char *str )
{
if (str == NULL) //这个判断很重要哦,小心你的指针指向非法空间出现段错误··
{
m_data = new char[1];
*m_data = '\0';
}
else
{
length = strlen ( str );
m_data = new char [length + 1];
strcpy ( m_data, str );
}
}
如果您不想做此步的判断,那么就可以选择使用第二种方式定义默认构造函数。
2)通过函数重载来定义另一个构造函数-----一个没有参数的构造函数
e.g:
String (); //类体中声明;
String :: String ()
{
m_data = new char[1]; //空间任意开辟
length = 4;
}
注:
若在此处给指针变量开辟了空间,那么在以后其他代码实现部分,要对此指针所指向的地址内容进行修改操作时,要先delete释放原有空间,再重新开辟。若不释放,编译可以通过,运行也正常,但是会造成内存泄漏。
由于只能有一个默认构造函数,所以不能同时采用以上两种方式。编译时会出错。提示是在重载默认构造函数时有二义性:
String.cpp: In function ‘int main()’:
//main函数中隐式定义String ob2;
String.cpp:95: error: call of overloaded ‘String()’ is ambiguous
String.cpp:29: note: candidates are: String::String(const char*)
String.cpp:22: note: String::String()
TIPS:
设计类时通常应该提供对所以类成员做隐式初始化的默认构造函数。
使用上述任何一种方式(无参或所以参数都有默认值)创建了默认构造函数后,就可以隐式初始化:
e.g: String ob2;
但是,不要被非默认构造函数的隐式形式所误导:
e.g: String ob1(“wuyun”); //接收参数的构造函数
String ob2 ( ); // 返回String对象的函数
参考文献: 《C++ Primmer Plus》第五版