构造函数主要用来初始化对象,析构函数主要用来善后对象的消亡。
特点:
对象消亡时,析构函数自动被调用,在对象消亡前做一些善后工作,比如释放分配的空间。
在定义类的时候如果没有写析构函数,则编译器会自动生成缺省析构函数(并不会做什么事)。
class String
{
private:
char * p;
public:
String()
{
p=new char[10];
}
~ String(); //定义析构函数
};
String::~ String()
{
delete [] p; //释放动态内存
}
对象数组的生命周期结束时,对象数组的每个元素的析构函数都会被调用。
使用delete运算也会导致析构函数被调用。
Ctest * pTest;
pTest = new Ctest; //构造函数调用
delete pTest; //析构函数调用
pTest = new Ctest[3]; //构造函数调用3次
delete [] pTest; //析构函数调用3次
class Demo
{
int id;
public:
Demo(int i)
{
id=i;
cout<<"id="<
class CRectangle
{
private:
int w,h;
static int nTotalArea; //静态成员变量
static int nTotalNumber;
public:
CRectangle(int w_,int h_);
~CRectangle();
static void PrintToal(); //静态成员函数
};
静态成员变量为所有对象共享。(sizeof函数不会计算静态成员变量)
静态成员函数并不具体作用于某个对象。
静态成员不需要通过对象就能访问。
访问静态成员方法:
CRectangle::PrintTotal(); //不用通过具体对象
CRectangle r; //也可以构造对象后通过对象访问
r.PrintTotal();
CRectangle * p=&r; //通过指针访问
p->PrintTotal();
CRectangle & ref = r; //通过引用访问
int n = ref.nTotalNubmber;
静态成员变量本质上是全局变量,哪怕没有对象,静态成员变量也存在。
静态成员函数本质上是全局函数。
class CRectangle
{
private:
int w,h;
static int nTotalArea; //静态成员变量
static int nTotalNumber;
public:
CRectangle(int w_,int h_);
~CRectangle();
static void PrintToal(); //静态成员函数
};
CRectangle::CRectangle(int w_,int h_) //构造函数
{
w=w_;
h=h_;
nTotalNumber ++;
nToalArea += w*h;
}
CRectangle::~CRectangle() //析构函数
{
nTotalNumber --;
nTotalArea -= w*h;
}
void CRectangle::PrintTotal()
{
cout<
静态成员函数中,不能访问非静态成员变量,不能调用非静态成员函数。(不清楚非静态成员变量属于哪一个类)
上面的代码没有写复制构造函数,肯会造成返回CRectangle类对象的时候,系统调用默认的复制构造函数,造成没有加nTotalNumber和nTotalArea。
class CTyre
{
private:
int radius;
int width;
public:
CTyre(iny r,int w):radius(r),width(w){} //初始化列表(新方式初始化)
};
class CEngine
{
};
class CCar
{
private:
int price;
CTyre tyre;
CEngine engine;
public:
CCar(int p,int tr,int tw);
};
CCar::CCar(int p,int tr,int tw):price(p),tyre(tr,w) //初始化列表
{
};
int main()
{
CCar car(20000,17,225); //价格20000,19半径,225宽度
return 0;
}
如果上面的CCar类没有定义构造函数的话,编译器不知道CCar里的tyre是如何初始化的,会导致编译出错(engine成员对象没问题,因为它使用默认构造函数)。
生成封闭类对象时:首先执行所有对象的构造函数(与在封闭类中说明顺序一致,与初始化列表中的出现顺序无关),然后执行封闭类的构造函数。
封闭类对象消亡时:首先执行封闭类的析构函数,然后执行成员对象的析构函数。
即析构函数与构造函数的调用顺序相反,先构造的后消亡。
友元分为:
一个类的友元函数可以访问该类的私有成员
class CCar; //提前声明,方便后面CDriver类使用
class CDriver
{
public:
void Modify(CCar * pCar); //改装汽车
};
class CCar
{
private:
int price;
friend int MostExpensiveCar(CCar cars[],int total); //声明友元函数
friend void CDriver::ModifyCar(CCar * pCar); //声明友元函数,使ModifyCar()也可以访问
};
void CDriver::ModifyCar(CCar * pCar)
{
pCar->price += 1000; //改装汽车后价值增加
}
int MostExpensiveCar(CCar cars[],int total)
{
int tmpMax = -1;
for (int i=0;itmpMax)
tmpMax=cars[i].price;
return tempMax;
}
int main()
{
return 0;
}
除了友元函数,还可以定义一个类为另一个类的友元。
A是B的友元类,则A的成员函数可以访问B的私有成员。
class CCar
{
private:
price;
friend class CDriver;
};
class CDriver
{
public:
CCar myCar;
void ModifyCar()
{
myCar.price += 1000;
}
};
int main()
{
return 0;
}
早期编译C++程序时需要将C++程序翻译成C程序,会把成员函数变成全局函数,其中全局函数会比成员函数多一个this指针。
翻译过程示例:
//C++程序
class CCar
{
public:
int price;
void SetPrice(int p);
};
void CCar::SetPrice(int p)
{
price=p;
}
int main()
{
CCar car;
car.SetPrice(20000);
return 0;
}
//C程序
struct CCar
{
int price;
};
void SetPrice(struct CCar * this,int p) //相比成员函数加了个this指针
{
this->price=p;
}
int main()
{
struct CCar car;
SetPrice(&car,20000);
return 0;
}
下面的程序需要运用this指针来理解
class A
{
int i;
public: void Hello(){cout<<"hello"<Hello(); //但成员函数Hello翻译为C程序后,并没有使用到p,所以可以这样使用
}
静态成员函数中不能使用this指针!
因为静态成员函数并不作用于某个对象!
因此,静态成员函数的真实的参数个数就是程序中写出的参数个数。
如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加上const关键字。
class Demo
{
private:
int value;
public:
void SetValue(){}
};
const Demo Obj; //常量对象
在类的成员函数说明后面可以加上const关键字,则该成员函数称为常量成员函数。
常量成员函数执行期间不应修改其作用的对象。因此,在常量成员函数中不能修改成员变量的值(静态成员变量除外),也不能调用同类的非常量成员函数(静态成员函数除外)。
class Sample
{
public:
int value;
void GetValue() const;
void func(){};
Sample(){}
};
void Sample::GetValue() const
{
value=0; //wrong,不能修改成员变量的值
func(); //wrong,不能调用非常量成员函数
}
int main()
{
const Smaple o;
o.value=100; //wrong,常量对象不可被修改
o.func(); //wrong,常量对象上不能执行非常量成员函数
o.GetValue(); //OK,可以调用常量成员函数
return 0;
}
两个成员函数,名字和参数都一样,但是一个是const,一个不是,那么这两个成员函数算重载。
class CTest
{
private:
int n;
public:
CTest(){n=1;}
int GetValue() const {return n;}
int GetValue() {return 2*n;}
};
int main
{
const CTest objTest1;
CTest objTest2;
cout<
在引用前面可以加上const关键字,称为常引用。不能通过常引用修改引用的变量。常用作函数形参。
一般对象作为函数参数使,生成该参数需要调用复制构造函数,效率比较第。如果用指针作为参数,代码又不好看,这时可以用引用作为参数。如:
class Sample
{
……
};
void PrintObj(Sample & o)
{
……
}
但对象引用作为函数参数有一定风险性,在函数内若修改了形参o,那么实参也会改变,这时就可以用对象的常引用作为参数,如:
class Sample
{
……
};
void PrintObj(const Sample & o)
{
……
}
objTest2;
cout<
}
* 常引用
在引用前面可以加上const关键字,称为常引用。不能通过常引用修改引用的变量。常用作函数形参。
一般对象作为函数参数使,生成该参数需要调用复制构造函数,效率比较第。如果用指针作为参数,代码又不好看,这时可以用引用作为参数。如:
~~~c++
class Sample
{
……
};
void PrintObj(Sample & o)
{
……
}
但对象引用作为函数参数有一定风险性,在函数内若修改了形参o,那么实参也会改变,这时就可以用对象的常引用作为参数,如:
class Sample
{
……
};
void PrintObj(const Sample & o)
{
……
}