虽然接触cpp也有一段时间了,今天在进行复习回顾的时候,在一些细节上还是有点欠缺,特此整理。
一、默认构造函数
1.如果在定义类的时候没有指定默认构造函数,系统就会自动生成一个构造函数,函数体为空,表示对数据成员不做任何操作;
2.如果定义了构造函数,则系统不会自动生成一个构造函数;
3.参数全部默认的构造函数只能有一个,换句话说,可以不提供参数而调用的构造函数只能有一个;
//下面两种是不能同时出现的
test(double x=1,double y=1,double z=1);
test();
//构造函数可以重载,但是具有默认参数与没有默认参数的相同形式的构造函数是不同的。但是,每一个个对象调用的构造函数必须唯一;
Tri(double x,double y=5,double z=5); //A
Tri(double x,double y,double z); //B
Tri(); //C
/*
*Tri Tria; //正确,调用C
*Tri Tria(4); //正确,调用A
*Tri Tria(4,5,6); //错误,A行与B行均满足调用条件,出现歧义;
*/
二、析构函数
1.析构函数也是一个特殊的成员函数,他的作用与构造函数相反,是在撤销对象占用的内存之前完成的一些清理工作,使得这部分内存可以被程序重新分配。且,一个类中只能有一个析构函数;
2.析构函数的调用顺序正好与构造函数相反,先构造的对象后析构,后构造的对象先析构
3.一般的类可以不需要定义析构函数,即采用默认析构函数即可,但是,如果在构造函数中使用了new运算符为对象的成员数据动态分配了存储空间,则一定要在类中显示的地定义析构函数,在撤销对象时将new分配的空间用delete释放。
三、构造对象的顺序
1.与变量的作用域一样“{<一般生命周期在一对大括号内有效>}”,对象的作用域也有全局和局部之分,其中局部对象又分为静态局部对象与动态局部对象>
1).全局对象在main函数执行之前被构造。
2).静态局部对象在进入其作用域时被构造,且只能构造一次,在程序结束后才被析构;
3).动态局部对象在进入其作用域时被构造,在作用域结束后被析构;
所以,在日常学习与实验中,要注意每个参数的声明周期,同时需要理解的是,自己定义的类与int、double等是一样的,具有相似的性质,可以将自定义类看作是“int”或者“double”等;
class tri
{
<do something>;
};
Tri *p=new Tri; //动态的开辟内存
调用顺序实验
#include<iostream>
#include<string>
using namespace std;
class STR
{
char* p;
public:
STR(char* s);
~STR();
};
STR::STR(char* s)
{
p = new char[strlen(s) + 1];
strcpy(p, s);
cout << "调用" << p << "的构造函数" << endl;
}
STR::~STR()
{
if (p)
{
cout << "调用" << p << "的析构函数" << endl;
delete[]p;
}
}
STR s1("全局对象");
void fun()
{
static STR s2("局部静态对象");
STR s3("局部动态对象");
}
int main()
{
cout << "main begin ..." << endl;
fun();
fun();
cout << "mian end ..." << endl;
return 0;
}
四、 const 成员与对象
4.1 const 成员数据
1.用const声明的成员数据,称为常成员数据,其值** 必须使用参数初始化列表的构造函数进行初始化**并且这个值在程序运行过程中不能被改变。
4.2 const成员函数
如果要求成员函数中所有成员数据的值都不允许改变,则可以将这个成员函数定义为const成员函数,称为常成员函数;
常成员函数不能调用另一个非const成员函数.
//类名::类型名 函数名(参数表) const
class Tri
{
private:
double a,b,c;
public:
Tri(double x,double y,double z);
double Peri() const
{return a+b+c;} //函数内不能改变成员函数a,b,c的值;
};
4.3 const 对象
如果要求类的对象在程序运行过程中一直保持初始值,可以将这个对象定义为常对象。
类名 const 对象名(<实参1,实参2,...>)
或
const 类名 对象名(<实参1,实参2,...>)
//推荐使用第一种,第一种直接加在对象名之前表示该对象是常对象,第二种是容易产生歧义;
1.常对象除了由系统自动调用构造函数和析构函数外,只能调用他所在类的常成员函数,而不能调用普通成员函数;
2.如果一定要修改常对象中,某个数据成员的值,可以将该数据成员声明为mutable,即可变的数据成员.,然后可以在成员函数中进行修改;
#include<iostream>
#include<cmath>
using namespace std;
class Tri
{
private:
double a,b;
mutable double c; //声明c为可变的数据成员;
public:
Tri(double x,double y,double z);
double Peri() const; //常成员函数
double Area();
...
};
int main()
{
Tri const tria(3,4,5); //创建三角形常对象tria
Tri trib(5,5,5); //普通对象
...
}
五、静态成员
如果将类中的某一成员数据的存储类型指定为静态类型时,这个成员数据就为各对象所共有,在内存中只占一份空间,他的值对所有的对象都是一样的,所有对象都可以引用他,相当于类中的“公共数据”;
#include<iostream>
using namespce std;
class Tri
{
private:double a,b,c;
static int n; //公有静态成员数据;
...
};
int Tri::n; //静态数据成员必须要在类外进行初始化,默认值是0;
...
说明:
1)在类中,静态成员数据的空间是公共的、唯一的,属于所有同类对象。
2)静态成员数据必须要在类体外进行初始化说明,初始化的格式为:
数据类型 类名::静态成员数据=初值;<默认值为0>
3)静态成员数据受类的访问权限限制。例如,可以将静态成员数据类型声明为private,此时,在类外不能访问该成员;
4 )静态成员数据属于类,被对象共享,定义了类以后,即使还没有创建对象,类的静态成员数据已经存在,并且可以被访问。
六、友元函数友元类
6.1友元函数
6.1.1普通函数声明为类的友元函数
有时类外函数需要访问类的私有成员数据,可以将该函数声明为友元函数:
class <className>
{
...
friend 函数类型 函数名 (<参数列表>); //在类体中声明类的友元函数
...
};
友元函数是类外函数,不是相应类的成员函数,没有类体中的this指针,需要用对象名.成员名的形式访问类中的成员数据,同时,其形参一般是对象的引用形式。
6.1.2其他类的成员函数声明为类的友元函数
class A
{
...
friend 函数类型B::函数名 (<参数列表>); //在类体中声明类的友元函数
...
};
6.2 友元类
声明一个友元类之后,友元类可以自由的引用这个类的所有成员;
例如:类B 是类A的友元类,则需要在类A中声明类B,并且一般先定义类A,在定义类B,同时对类B要作提前声明
class A
{
...
friend class B;
...
}
#include<iostream>
using namespace std;
...
class B; //首先声明类B
class A //定义类A
{
...
};
class B //定义类B
{
...;
}
注意:
1)友元的关系是单向的而不是双向的,类A是类B的友元,不等于类B是类A的友元;
2) 友元的关系不能传递。