程序员在成员函数中处理非静态数据成员,隐式的类对象就会发生。比如:
void Point3d::translate(const Point3d:: &pt){
x += pt.x;
y += pt.y;
z += pt.z;
}
事实上x、y、z的存取是经由this指针完成的,其函数参数可以理解为:
void Point3d::translate(Point3d * const this, const Point3d:: &pt){
this->x += pt.x;
this->y += pt.y;
this->z += pt.z;
}
构造函数是特殊的成员函数,与其它成员函数不同,构造函数和类同名,而且没有返回类型,一个类可以有多个构造函数,每个构造函数必须有与其他构造函数不同的参数数目或类型的形参。
若使用编译器自动生成的默认构造函数(或自己定义一个未进行任何操作的默认构造函数),则类中每个成员,使用与初始化变量相同的规则来进行初始化。
例。下列代码中a、b的各个成员变量的值是多少?
class Student{
public:
Student(){}
void show();
private:
string name;
int number;
int score;
};
Student a;
int main(){
Student b;
}
解析:a中number和score初始化为0,而b是局部对象,故b中number和score不被初始化,为垃圾值。
在冒号和花括号之间的代码成为构造函数的初始化列表。构造函数的初始化列表为类的一个或多个数据成员指定初值。它跟在搞糟函数的形参之后,以冒号开始。构造函数的初始化式是一系列成员名,每个成员后面是括在圆括号中的初始值。多个成员的初始化用逗号分隔。
例。运行下面的C++代码,其输出结果是什么?
class A{
private:
int i;
int j;
public:
A():j(0), i(j+2){}
void print(){
cout<<i<<" "<<j<<endl;
}
};
int main(){
A a;
a.print();
return 0;
}
解析:i是一个内存中的垃圾数字,而j为0。在C++中,成员变量的初始化顺序与变量在类型中的声明顺序相同,而于它们在构造函数的初始化列表中的顺序无关。
没有默认构造函数的类类型的成员,以及const类型的成员变量和引用类型的成员变量,都必须在构造函数初始化列表中进行初始化。
复制构造函数、赋值操作符和析构函数宗成伟赋值控制。编译器自动实现这些操作,但是类也可以定义自己的版本。
复制构造函数可用于:
string null_book1("9-9-999-9"); //直接初始化
string null_book2=null_book1; //复制初始化
string null_book3(null_book1); //复制初始化
string null_book3; //调用默认构造函数创建一个空字符串对象
null_book3 = null_book1; //不是调用复制构造函数,而是利用赋值运算符将null_book1赋值给null_book3
vector<string> svec(10);
例。下面代码的输出结果是()。
class Myclass{
public:
Myclass(int n){number = n;}
Myclass(const Myclass &other){
number=other.number;
cout<<"a ";
}
private:
int number;
};
void fun(Myclass p){
Myclass temp(p);
}
int main(void){
Myclass obj1(10), obj2(0);
Myclass obj3(obj1);
fun(obj3);
return 0;
}
解析:a a a 。调用三次拷贝构造函数,第一次是main中Myclass obj3(obj1); ,第二次是实参obj3到fun形参p,第三次是函数fun中的Myclass temp§;语句。
派生类名(总参数表):基类构造函数(参数表){
//函数体
}
具体的调用顺序为:
class A{
public:
A(){cout<<"A";};
~A(){cout<<"~A";}
};
class B{
public:
B(A &a):_a(a){ //__a(a)调用了拷贝构造函数
cout<<"B";
};
~B(){cout<<"~B";}
private:
A _a;
};
int main(void){
A a;
B b(a);
return 0;
}
解析:ABBA~A。构造过程:A A B,那么析构过程为: B A A。注意之所以构造了两个A,是因为“a _(a)”调用了拷贝构造函数对B类对象中A初始化,而拷贝构造函数采用的是系统自动生成的版本,没有输出。
对类层次中的同名成员函数来说,有3中关系:
class A{
...
virtual int fun();
void fun(int);
void fun(double, double);
static int fun(char);
}
成员函数重载的特征:
覆盖(override)
覆盖是指在派生类中覆盖基函数中的同名函数,要求基函数必须是虚函数,且:
1)与基类虚函数具有相同的参数个数;
2)与基类的虚函数具有相同的参数类型;
3)与基类的虚函数具有相同的返回类型。
class A{
public:
virtual void fun1(int ,int){}
};
class B:public A{
public:
void fun1(int ,int){}
};
覆盖的特征如下:
1)不同的范围(分别位于派生类与基类);
2)相同的函数名字;
3)相同的参数;
4)基类函数必须有vitural关键字。
重载与覆盖的区别如下:
隐藏(oversee)
隐藏指的是在某些情况下,派生类中的函数屏蔽了基类中的同名函数,这些情况包括:
1)两个函数参数相同,但基类函数不是虚函数。和覆盖的区别在于基类函数是否是虚函数。如下述代码:
class A{
public:
void fun(int xp){ //虚成员函数fun,参数为int型
cout << xp << endl;
}
};
class B : public A{ //类B由类A派生而来
public:
void fun(int xp){} //隐藏父类的fun函数
}
上述代码若有:
B b;
b.fun(2);
则调用的讲师B中的函数fun,若需要调用A中的函数fun,可以以这种形式调用:
B b;
b.A::fun(2);
2)两个函数参数不同,无论基类函数是否是虚函数,基类函数都会被屏蔽。和重载的区别在于两个函数不在同一类中。