构造函数
构造函数的主要功能是为对象分配空间,其次也可以对类的数据成员进行初始化。构造函数有如下性质:
1.构造函数的名字必须与类名相同。
2.构造函数的参数可以是任何数据类型,但是没的返回值,不能为它定义返回类型,包括void型在内。
3.当创建对象时,编译系统会自动地调用构造函数完成对象内存空间的分配和初始化工作。(如果是系统默认构造函数,成员变量初始化为没有意义的随机值)
4.构造函数是类的成员函数,具有一般成员函数的所有性质,可访问类的所有成员。可以是内联函数,可以带有参数表,可以带有默认的形参值,还可以重载。
带有默认参数的构造函数
可以在声明时,也可以在定义时带有默认的参数值。这样在定义对象时可以不指定实参,用默认的参数值来初始化数据成员。
例1(声明时默认):
#include<iostream>
using namespace std;
class Rectangle
{
double length,width;
public:
Rectangle(double a=0.0,double b=0.0);
double area();
};
Rectangle::Rectangle(double aa,double bb)
{
length = aa;
width = bb;
}
double Rectangle::area()
{
double s;
s = length * width;
return s;
}
int main()
{
Rectangle ob1;
Rectangle ob2(2.5);
Rectangle ob3(5.0,4.0);
cout<<ob1.area()<<endl;
cout<<ob2.area()<<endl;
cout<<ob3.area()<<endl;
system("pause");
}
例2(定义时默认):
#include<iostream>
using namespace std;
class Rectangle
{
double length,width,value;
public:
Rectangle(double a,double b);
double area();
};
Rectangle::Rectangle(double aa,double bb = 10)//定义带有默认参数的构造函数
{
length = aa;
width = bb;
}
double Rectangle::area()
{
return length*width;
}
int main()
{
Rectangle ob2(2.5),ob3(5.0,4.0);
cout<<ob2.area()<<endl;
cout<<ob3.area()<<endl;
system("pause");
}
拷贝构造函数
用已知的对象去创建一个未定义过的新对象。
自定义拷贝构造函数的形式:
类名(类名 &对象名)
{
函数体
}
如果没有用户自定义的,则系统会自动调用默认的的拷贝构造函数,默认构造函数是浅拷贝。(新老对象指向同一个引用)。
一般来说,以下三种情况拷贝构造函数会被调用:
1.用类的对象去初始化该类的另一个对象时。
2.函数的形参是类的对象,函数调用过程中进行形参和实参的结合时。
3.函数返回值是类的对象,函数执行完进行返回时。
#include<iostream>
using namespace std;
class Dot
{
private:
int a,b;
public:
Dot(int x=0,int y=0)
{
a=x;
b=y;
}
Dot(Dot &d);
int geta()
{
return a;
}
int getb()
{
return b;
}
};
Dot::Dot(Dot &d){
a=d.a+10;
b=d.b+10;
cout<<"调用拷贝构造函数"<<endl;
}
void f(Dot p)
{
cout<<p.geta()<<" "<<p.getb()<<endl;
}
Dot g()
{
Dot q(3,5);
return q;
}
int main()
{
Dot dt1(2,4);
Dot dt2(dt1);//调用拷贝构造函数的第1种情况
cout<<dt2.geta()<<" "<<dt2.getb()<<endl;
f(dt2);//调用拷贝构造函数的第2种情况
cout<<dt2.geta()<<" "<<dt2.getb()<<endl;
dt2=g();//调用拷贝构造函数的第3种情况 //这句在DEV c++中报错,在VC60下不报错。no matching function for call to `Dot::Dot(Dot)'
cout<<dt2.geta()<<" "<<dt2.getb()<<endl;
dt2=dt1;//位拷贝,值传递 没用调用拷贝构造函数!!!!!!!因为在此之前,dt2已经存在。
cout<<dt2.geta()<<" "<<dt2.getb()<<endl;
Dot dtx=dt1;//在此之前dtx不存在,调用拷贝构造函数。 如果写成Dot dtx; dtx=dt1;则不会调用拷贝构造函数,与dt2=dt1这行代码一样的处理。
cout<<dtx.geta()<<" "<<dtx.getb()<<endl;
system("pause");
}
Thinking in c++中的demo:
#include<fstream>
using namespace std;
ofstream out("Howmany2.out");
class Howmany2{
string name;
static int objectCount;
public:
Howmany2(const string& id=""): name(id){
++objectCount;
print("Howmany2()");
}
~Howmany2(){
--objectCount;
print("~Howmany2()");
}
Howmany2(const Howmany2& h):name(h.name){
name+=" copy";
++objectCount;
print("Howmany2(const Howmany2&)");
}
void print(const string& msg="")const{
if(msg.size()!=0)
out<<msg<<endl;
out<<'\t'<<name<<":"<<"objectCount="<<objectCount<<endl;
}
};
int Howmany2::objectCount=0;
Howmany2 f(Howmany2 x){
x.print("x argument inside f()");
out<<"Returning from f()"<<endl;
return x;
}
int main(){
Howmany2 h("HK");
out<<"Entering f()"<<endl;
Howmany2 h2=f(h);
h2.print("h2 after call to f()");
out<<"Call f(),no return value"<<endl;
f(h);
out<<"after call to f()"<<endl;
}
结果如下:
Howmany2()
HK:objectCount=1
Entering f()
Howmany2(const Howmany2&)
HK copy:objectCount=2
x argument inside f()
HK copy:objectCount=2
Returning from f()
Howmany2(const Howmany2&)
HK copy copy:objectCount=3
~Howmany2()
HK copy:objectCount=2
h2 after call to f()
HK copy copy:objectCount=2
Call f(),no return value
Howmany2(const Howmany2&)
HK copy:objectCount=3
x argument inside f()
HK copy:objectCount=3
Returning from f()
Howmany2(const Howmany2&)
HK copy copy:objectCount=4
~Howmany2()
HK copy copy:objectCount=3
~Howmany2()
HK copy:objectCount=2
after call to f()
~Howmany2()
HK copy copy:objectCount=1
~Howmany2()
HK:objectCount=0
析构函数
1.析构函数不能被重载,一个类中只有一个析构函数。
2.当撤销对象时,系统会自动调用析构函数完成空间的释放和善后工作。
3.每个类都需要有一个析构函数,如果没有显式地定义,则系统会自动生成一个默认的析构函数,它是一个空函数。
4.对于大多数类而言,默认的析构函数就能满足要求,但如果对象在完成操作前需要做内部处理,则应显示定义析构函数。
5.构造函数和析构函数常见的用法是,在构造函数中用new运算符为对象分配空间,在析构函数中用delete去处符释放空间。(用new创建对象调用构造函数,用delete销毁对象调用析构函数)。
综合例子:
#include <iostream>
using namespace std;
class B
{
public:
B(){cout<<"B"<<endl;}
~B(){cout<<"~B"<<endl;}
};
class C:public B
{
public:
C(B &b):b_(b){cout<<"C"<<endl;}
~C(){cout<<"~C"<<endl;}
private:
B b_;
};
int main(int argc, char* argv[])
{
//cout<<sizeof(A)<<endl;
B b;
C d(b);
system("pause");
return 0;
}
上例中,第1个B是在B b时产生的。
第2个B和第1个C是C d(b)时产生的。在初始化表达式表中,b_(b)没有调用B的构造函数,而是调用了B的默认拷贝构造函数。
而析构函数中却是所有的对象都调用的析构函数,且与其构造时的顺序相反。