1.类的构造函数
对象的建立过程:变量在程序运行时会占据空间。声明变量时对变量进行初始化时,分配内存单元和写入变量初始值是同时进行的。
构造函数的作用:在对象被创建时利用特定的值构造对象,将对象初始化为一个待定的状态。
构造函数的理解:1.构造函数也是类的一个成员函数(但是:1.函数名与类名相同。2.没有返回值。3.一般为共有函数。) 2.在对象被创建时会被自动调用。 3.构造函数决定初始化的方式。
构造函数的声明和定义:
声明:
class Clock{
public:
Clock (int newH, int newM, int newS);//有参数的构造函数
private:
int hour,minute,second;
};
实现:
Clock::Clock(int newH,int newM, int newS)//注意:参数名不能和成员名相同。一般在成员名前加m_
{
hour=newH;
minute=newM;
second=newS;
}
变量的初始化:
int main()
{
Clock c(0,0,0);//因为定义了形参,所以在c后面必须要有实参。
//所以不能用Clock c;来声明对象。
}
而无参数的构造函数:
声明:
Clock()
{
hour=0; minute=0; second=0;
}
实现同上
变量初始化:Clock c;
也可以用函数的重载将上面两种构造函数定义在同一个类中,这样变量的初始化就有两种方式了。
使用构造函数的方式:
1.显式调用:Clock a =Clock(0,0,0);
2.隐式调用:Clock a (0,0,0);
所以说构造函数决定变量初始化的方式。
《C++ Primer Plus》p352-355,《C++语言程序设计》p107-109.
2.复制构造函数
复制构造函数:其形参是本类的对象的引用。作用是使用一个已经存在的对象(由复制构造函数的参数指定),去初始化同类的一个新对象。
隐含复制函数:功能是把初始值对象的每个数据成员的值都复制到新建立的对象中。
声明和实现复制构造函数的一般方法:
class 类名
{
public:
类名(形参表);//构造函数
类名(类名&对象名);//复制构造函数
...
};
类名::类名(类名&对象名);//复制构造函数的实现
{函数体}
实例:
定义:
Class Point{
pubilc:
Point(int xx=0,int yy=0) //构造函数
{
x=xx;
y=yy;
}
Point(Point &p); //复制构造函数
int getX() {return x;}
int getY() {return y;}
private :
int x,y;};
实现:
Point::Point(Point &p)
{
x=p.x;
y=p.y;
}
复制构造函数在下面3种情况下会被调用:
(1)用类的一个对象去初始化该类的另一个对象时。
Point a(1,2);
Point b(a);
Point c=a;
a(1,2)是用构造函数来对对象a进行初始化。而b(a)和c=a就是用对象a初始化对象b和c,这时复制函数被调用。
注意:Point c=a;是初始化语句,不是赋值语句。
(2)函数的形参是类的对象,调用函数时,进行形参和实参结合。
void f(Ponit p)
{
cout<<p.getX()<<endl;
}
int main
{
Point a(1,2);
f(a); //函数的形参为类的对象,当调用函数时,复制构造函数被调用
return 0;
}
输出结果为:1
这是因为 f 函数的形参 a 在初始化时调用了复制构造函数。
前面说过,函数的形参的值等于函数调用时对应的实参,现在可以知道这不一定是正确的。如果形参是一个对象,那么形参的值是否等于实参,取决于该对象所属的类的复制构造函数是如何实现的。
以对象作为函数的形参,在函数被调用时,生成的形参要用复制构造函数初始化,这会带来时间上的开销。如果用对象的引用而不是对象作为形参,就没有这个问题了。但是以引用作为形参有一定的风险,因为这种情况下如果形参的值发生改变,实参的值也会跟着改变。
只有把对象用值传递时,才会调用复制构造函数。所以传递比较大的对象时,传递引用会比传值的效率高很多。
如:
void f(const,Point &p)
{...}
这样f函数中出现任何有可能导致 c 的值被修改的语句,都会引发编译错误。
(3)函数的返回值是类的对象,函数执行完成返回调用者时。
Point g()
{
Point a(1,2);
return a; //函数返回值是类对象,返回函数值时,调用复制构造函数
}
原理有关于对象的生存期。
《C++语言程序设计》p111-112
3.析构函数
构造函数创建完对象后,程序会一直跟踪,直到过期。而对象过期时,会调用析构函数来完成清理工作。
比如说构造函数使用了new,则析构函数就有delete来释放内存。如果没有使用,则只需用隐式析构函数即可。
析构函数原型:~Stock():
Stock::~Stock()
{}
不同存储类型调用析构函不同:
静态储存类对象——>结束时自动被调用
自动存储类对象——>完成代码块时自动被调用
通过new创建的——>将它驻留在栈内存或自由存储区中,当使用delete来释放内存时,自动被调用
创建临时对象——>结束对该对象的使用时自动被调用