C++学习笔记1:构造函数和析构函数

来源:互联网 发布:编写js的工具 编辑:程序博客网 时间:2024/05/17 04:03

     在C++语言中,构造函数主要起初始化对象的作用。当一个对象的生命周期结束时,我们应该去释放这个对象

占用的资源,这可以利用析构函数来完成。下面我们以几个具体的例子来深入讲解C++的构造函数和析构函数。

 

 

1        构造函数

#include <iostream.h>

class point

{

      int x;

      int y;

public:

      point()        //构造函数

      {

           x=0;

           y=0;

      }

      void output()

      {

           cout<<x<<endl<<y<<endl;

      }

 

};

 

void main()

{

      point pt;

      pt.output();

}

 

运行结果:

0

0

 

分析:

(1)       构造函数与类同名

(2)       构造函数的作用是对对象本身做初始化工作,也就是给用户提供初始化类中成员变量的一种方式

(3)       如果一个类中没有定义任何一的构造函数,那么C++编译器在某些情况下会为该类提供一个默认的构造函数,这个默认的构造函数是一个不带参数的构造函数。只要一个类中定义了一个构造函数,不管这个构造函数是否是带参数的构造函数,C++编译器就不再提供默认的构造函数。也就是说,如果为一个类定义了一个带参数的构造函数,还想要无参数的构造函数。

 

2        析构函数

class Student

{

private:

      char *pName;

public:

      Student()    //构造函数

      {

           pName=New char[20];

      }

      ~Student()  //析构函数

      {

           delete[] pName;

      }

};

析构函数不允许带参数,并且一个类中只能有一个析构函数。

 

3        Point类的拷贝构造函数

#include<iostream>

using namespace std;

class Point

{

public:

      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;

      cout<<"拷贝构造函数被调用"<<endl;

}

 

void fun1(Point p)                        //形参为Point类的函数

{

      cout<<p.GetX()<<endl;

}

 

Point fun2()                             //返回值为Point类对象的函数

{

      Point A(1,2);

      return A;

}

 

int main()

{

      Point A(4,5);

      Point B(A);           //情况一,用A初始化B。调用拷贝构造函数

      cout<<B.GetX()<<endl;

      fun1(B);              //情况二,对象B作形参。调用拷贝构造函数

      B=fun2();             //情况三,返回值是类对象,函数返回时,调用拷贝构造函数

      cout<<B.GetX()<<endl;

}

 

运行结果:

拷贝构造函数被调用

4

拷贝构造函数被调用

4

拷贝构造函数被调用

1

 

4        类的组合,Line

源程序:

#include<iostream>

#include<cmath>

using namespace std;

 

class Point

{

public:

      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;

      cout<<"Point拷贝构造函数被调用"<<endl;

}

//类的组合

class Line

{

public:

      Line(Point xp1,Point xp2);  //构造函数

      Line(Line &);

      double GetLen() {return len;}

private:

      Point p1,p2;

      double len;

};

//组合类的构造函数的实现

Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2)

{

      cout<<"Line构造函数被调用"<<endl;

      double x=double(p1.GetX()-p2.GetX());

      double y=double(p1.GetY()-p2.GetY());

      len=sqrt(x*x+y*y);

}

 

//组合类的拷贝构造函数

Line::Line(Line &L):p1(L.p1),p2(L.p2)

{

      cout<<"Line拷贝构造函数被调用"<<endl;

      len=L.len;

}

 

void main()

{

      Point myp1(1,1),myp2(4,5);        //建立Point类的对象

      Line line(myp1,myp2);             //建立Line类的对象

      Line line2(line);                 //对上一步进行拷贝并建立新对象

      cout<<"The length of the line is:";

      cout<<line.GetLen()<<endl;

      cout<<"The length of the line2 is:";

      cout<<line2.GetLen()<<endl;

}

 

运行结果:

Point拷贝构造函数被调用

Point拷贝构造函数被调用

Point拷贝构造函数被调用

Point拷贝构造函数被调用

Line拷贝构造函数被调用

Point拷贝构造函数被调用

Point拷贝构造函数被调用

Line拷贝构造函数被调用

The length of the line is:5

The length of the line2 is:5

 

5        对象数组

源程序:

//Point.h

#if!defined(_POINT_H)

#define _POINT_H

class Point

{

public:

      Point();

      Point(int xx,int yy);

      ~Point();

      void Move(int x,int y);

      int GetX() {return X;}

      int GetY() {return Y;}

private:

      int X,Y;

};

#endif

 

//Point.cpp

#include<iostream>

#include"Point.h"

using namespace std;

Point::Point()

{

      X=Y=0;

      cout<<"Default Constructor called."<<endl;

}

Point::Point(int xx,int yy)

{

      X=xx;

      Y=yy;

      cout<<"Constructor called."<<endl;

}

Point::~Point()

{

      cout<<"Destructor called."<<endl;

}

void Point::Move(int x,int y)

{

      X=x;

      Y=y;

}

 

//6_3.cpp

#include<iostream>

#include"Point.h"

using namespace std;

int main()

{

      cout<<"Entering main..."<<endl;

      Point A[2];

      for(int i=0;i<2;i++)

           A[i].Move(i+10,i+20);

      cout<<"Exiting main..."<<endl;

}

 

运行结果:

Entering main…

Default Constructor called.

Default Constructor called.

Exiting main…

Destructor called.

Destructor called.

 

6        函数调用

源程序:

#include<iostream.h>

class B

{

      int x,y;

public:

      B() {x=y=0;cout<<"Constructor1"<<endl;}

      B(int i) {x=i;y=0;cout<<"Constructor2"<<endl;}

      B(int i,int j) {x=i;y=j;cout<<"Constructor3"<<endl;}

      ~B() {cout<<"Destructor"<<endl;}

      void printf()

      {

           cout<<"x="<<x<<",y="<<y<<endl;

      }

};

 

void main()

{

      B *ptr;

      ptr=new B[3];

      ptr[0]=B();

      ptr[1]=B(5);

      ptr[2]=B(2,3);

      for(int i=0;i<3;i++)

           ptr[i].printf();

      delete[] ptr;

}

 

运行结果:

Constructor1

Constructor1

Constructor1

Constructor1

Destructor

Constructor2

Destructor

Constructor3

Destructor

x=0,y=0

x=5,y=0

x=2,y=3

Destructor

Destructor

Destructor


分析:

执行ptr=new B[3];调用第一个构造函数3次;

执行ptr[0]=B();创建一个无名对象,等价于{B temp; ptr[0]=temp; 释放temp},因此调用第一个构造函数1次,调用析构函数1次;

执行ptr[1]=B(5);创建一个无名对象,调用第二个构造函数1次,调用析构函数1次;

执行ptr[2]=B(2,3);创建一个无名对象,调用第三个构造函数1次,调用析构函数1次;

执行for循环语句输出相关内容;

最后执行delete[] ptr;删除对象数组ptr,调用析构函数3次。

 

7        派生类构造函数举例

#include<iostream>

using namespace std;

class B1

{

public:

      B1(int i) {cout<<"constructing B1 "<<i<<endl;}

};

class B2

{

public:

      B2(int j) {cout<<"constructing B2 "<<j<<endl;}

};

class B3

{

public:

      B3() {cout<<"constructing B3 *"<<endl;}

};

class C:public B2,public B1,public B3

{

public:

      C(int a,int b,int c,int d):B1(a),memberB2(d),memberB1(c),B2(b){}

private:

      B1 memberB1;                       //B1为类名,memberB1为对象

      B2 memberB2;

      B3 memberB3;

};

 

int main()

{

      C obj(1,2,3,4);

}

 

运行结果:

constructing B2 2

constructing B1 1

constructing B3 *

constructing B1 3

constructing B2 4

constructing B3 *

 

分析:

基类构造函数的调用顺序是按照派生类声明时的顺序,先B2B1B3

内嵌对象的构造函数调用顺序是按照成员在类中声明的顺序,先B1B2B3 

 

8        没有使用虚析构函数

源程序:

#include<iostream>

using namespace std;

 

class Base

{

public:

      ~Base() {cout<<"Base destructor/n";}

};

 

class Derived:public Base

{

public:

      Derived();

      ~Derived();

private:

      int *i_pointer;

};

 

Derived::Derived()

{i_pointer=new int(0);}

 

Derived::~Derived()

{

      cout<<"Derived destructor/n";

      delete i_pointer;

}

 

void fun(Base *b)

{delete b;}

 

int main()

{

      Base *b=new Derived();

      fun(b);

}

 

运行结果:

Base destructor

 

分析:

通过基类指针删除派生类对象时调用的是基类的析构函数,派生类的析构函数没有被执行,因此派生类对象中动态分配的内存空间没有得到释放,造成了内存泄漏。也就是说派生类对象成员i_pointer所指向的内存空间,在对象消失后既不能被本程序继续使用也没有被释放。对于内存需求量较大、长期连续运行的程序来说,如果持续发生这样的错误是很危险的,最终将导致因内存不足而引起程序的终止。   

 

9        使用虚析构函数

源程序:

#include<iostream>

using namespace std;

 

class Base

{

public:

      virtual ~Base() {cout<<"Base destructor/n";}

};

 

class Derived:public Base

{

public:

      Derived();

      ~Derived();

private:

      int *i_pointer;

};

 

Derived::Derived()

{i_pointer=new int(0);}

 

Derived::~Derived()

{

      cout<<"Derived destructor/n";

      delete i_pointer;

}

 

void fun(Base *b)

{delete b;}

 

int main()

{

      Base *b=new Derived();

      fun(b);

} 

 

运行结果:

Derived destructor

Base destructor

 

分析:

这说明派生类的析构函数被调用了,派生类对象中动态申请的内存空间被正确地释放了。这是由于使用了虚析构函数,实现了多态。