constructor && destructor
这两个函数是最基本的成员函数。它们与对象的生成与销毁相关。constructor用于创造对象,destructor用于销毁对象。这两个成员函数没啥好说的,只是简单易懂的概念与语法。
constructor
从时间上讲,只有当constructor完成它的工作时,对象才被创建,constructor用于创造对象。
当声明一个类的对象时,系统自动调用constructor。
default constructor:没有参数的constructor或者所有参数都有默认值的constructor,不需要显示初始化。 一个类只能有一个default constructor, 否则会因其模糊而无法通过编译。
如果你不提供一个default constructor,complier会自动为我们提供一个default constructor,它是空实现,不做任何事情。
归功于函数重载,一个类能有多个constructor,只能有一个default constructor,constructor没有返回值,没有声明类型。
explicit Stonewt(double); // conversion constructor
Stonewt(int stn, double bls);
Stonewt(); // default constructor
~Stonewt();
void sholw_lbs() const;
operator int() const; // convert type Stonewt to int
operator double() const; // convert type Stonewt to double
destructor
一个类只能有一个destructor。和constructor一样,它没有返回值,没有声明类型。当constructor中使用new/new [ ] 申请内存时, destructor必须使用delete/ delete[ ] 释放申请的空间。destructor很重要。
conversion constructor
conversion 有两个分支, another type to class type, or class type to another type.
此处采用another type而不是 built-in type 是因为我们也许会在两种类之间进行类型转换。
conversion constructor是只有一个参数的constructor,它用于将参数的类型转换为类的类型。在该成员函数的声明部分,我们常使用explicit 关键字表示显示调用,以免不必要的麻烦。explicit修饰符的使用,类似friend关键字,出现在头文件的声明部分。而在函数的具体定义处,它并不出现。
而在test file中,可以这样写
Stonewt mycat;
mycat = Stonewt(1.32); //ok
mycat = (Stonewt) 3.14; // ok
再来看将 converse class type to another type。注意它在头文件中的形式,注意它返回了对应类型的数值。其实就是运算符重载,和 operator<<没有太大的区别。
Stonewt::operator int() const
{
return int(pounds + 0.5);
}
Stonewt::operator double() const
{
return pounds;
}
以下是test file中的代码片段,我们可以这样使用该函数。就像对基本内置类型使用类型转换一样。
Stonewt obj1();
int rt = (int) obj1; // valid
int rtt = int (obj1); //valid
copy constructor && assignment operator
两者所做的事情很大程度上有重叠。在具体的实现上也容易混淆。
当类的private部分存在用以标识内存块的指针时,构造函数constructor用new 动态申请内存。
此时,copy constructor 和 operator=不能依赖complier 为我们生成,需要自己写,在函数中实现 deep copying,即对指针所指向的内容赋值,而非仅仅赋值指针的地址,以免当对象销毁时多次释放已经释放的内存。
copy constructor
copy constructor用于将一个对象赋值给一个新创建的对象。换言之,copy constructor用于初始化,包括在函数参数中传递值与返回值。
Class_name(const Class_name& ); // a general prototype for a copy constructor
当一个新对象被创建并且被初始化为一个已经存在的同类型对象时,copy constructor被使用。
较为明显的情况是将一个对象初始化为一个已经存在的对象
String ditto(motto); // given that motto is an existing object
String* pstring = new string(motto);
较不明显的情况是当一个程序生成一个对象的拷贝。这包括函数返回一个对象,函数接收对象的值传递,或者需要生成临时对象三种情况。因为传递值意味着创建原对象内容的拷贝。临时对象在不同编译器间情况不同,但当对象传递值时,它们都使用copy constructor。
给几个例子
return_type class_name::function_name (argument_list); // a method funciton prototype
// say , String is class name
String& String::add(const String& ) ; // pass-by-reference, return reference
String String::add(const String& ) ; // pass-by-reference, return value
String String::exchange(String ); // pass-by-value, return value
以上三个函数调用的都会使用copy constructor。三个函数都传递了值,需要对原有对象内容的进行拷贝。
所以,这里有一个结论:尽量传递引用,而非值。
一方面是为了效率,避免调用copy constructor, delete constructor,降低程序效率。
第二个原因是基类的指针或引用可以调用派生类的功能。(这个后面再细讲)。
assignment operator
assignment operator处理的是将一个对象赋值给同类型的另一个对象,处理的是赋值的情况。
注意赋值与初始化不同。当一个语句创建新对象时,它使用初始化;如果一个语句改变已存在对象的值,它就是赋值了。
String sirius;
String alpha = sirius; // initialization
String dogstar;
dogstar = sirius; // assignment
对于基本内置类型,对自我的赋值是正常的操作,但对类,需要在具体实现中考虑自我赋值的情况。当类的private成员中有指针变量时,assignment operator函数中通常需要 使用 delete [ ] 释放原有指针指向的空间,再重新申请一块新内容。
此处给出一个参考例子,并对operator可能的各种错误情况给出假设。
class String
{
private:
char* name;
int length;
publlic:
String();
String(const String&);
String& operator=(const String&);
}
String::String()
{
name = new char[4];
strcpy(name,"C++");
length = 3;
}
String& Stirng::operator=(const String& st)
{
if(st == *this)
return *this;
delete[] name;
length = st.length;
name = new char[length + 1];
strcpy(name, st.name);
return *this;
}
// other implementaiton
String obj1,obj2; // two object, both have a pointer variable
obj1 = obj2; // assignment operator
在见识了正确的书写示例后,再来看看对错误情况的分析。
此处给出几种错误的假设。
String sirius;
String alpha = sirius; // initialization
String dogstar;
dogstar = sirius; // assignment
第一种。我们可以不使用delete[] 语句删除先前指向的内容。直接改变指针的指向,但这样这块内容就无法再追踪了,它申请了,不再使用了,又不释放,且无法重新分配,会造成内存泄漏。
第二种。此外,对象obj1的指针指向了对象obj2的指针变量指向的内容。那么在两个对象结束时,destructor会释放已经释放的指针所指向的内存,这是一种编译错误。
第三,不书写delete[ ]语句,重新申请一块内存,并拷贝obj2的指针所指向的内容给obj1的指针。同样的,和第一种假设一样,直接失去了对obj1指针先前申请内存的追踪,造成内存泄漏。
所以,对于operator= ,我们需要self-assignment check, 当不是self -assignment时,需要先释放,再申请内存。
after comparison
单从结论上记忆的话,在assigement operator中,需要考虑self-assignment 的情况。且需要使用delete[ ]释放空间。而copy constructor因为用于初始化对象,对象本身的指针并未申请内存块,所以不需要且不能使用delete [ ] 语句。
other special member function
其他的special member funcitons还有address operator, 即operator&。它返回调用对象的地址,即this的值。这个没啥好讲的,它所做的,编译器为我们生成的,就是我们需要的。至少目前,我还没遇到需要特殊处理它的情况。
C++11 还有move constructor 和 assignment constructor,留待后续再讲。
In Summary
当我们学习类的知识时,学习它的特殊成员函数很有必要。它们是新的语法,也是工具。在掌握它们之后,我们会更亲近类,更了解类。随着使用,了解会越来越深,理解也会更到位。