原创 C/C++复习之路——王道程序员求职宝典——第八章《类》

文章目录

    • 类成员简介
      • 成员函数
      • 构造函数
      • 成员初始化列表
      • 复制构造函数
      • 析构函数
    • 成员函数的重载、覆盖与隐藏

类成员简介

成员函数

  程序员在成员函数中处理非静态数据成员,隐式的类对象就会发生。比如:

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;
}

构造函数

  构造函数是特殊的成员函数,与其它成员函数不同,构造函数和类同名,而且没有返回类型,一个类可以有多个构造函数,每个构造函数必须有与其他构造函数不同的参数数目或类型的形参。
  若使用编译器自动生成的默认构造函数(或自己定义一个未进行任何操作的默认构造函数),则类中每个成员,使用与初始化变量相同的规则来进行初始化。

  • 类成员:运行该类型的默认构造函数来初始化。
  • 内置或复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为0。

例。下列代码中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类型的成员变量和引用类型的成员变量,都必须在构造函数初始化列表中进行初始化。

复制构造函数

  复制构造函数、赋值操作符和析构函数宗成伟赋值控制。编译器自动实现这些操作,但是类也可以定义自己的版本。

  复制构造函数可用于:

  • 根据另一个同类型的对象初始化一个对象
    C++中支持两种初始化形式:直接初始化和赋值初始化。赋值初始化使用=符号,而直接初始化将初始化式放在圆括号中。例如:
    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§;语句。

析构函数

派生类名(总参数表):基类构造函数(参数表){
	//函数体
}

具体的调用顺序为:

  1. 完成对象所占整块内存的开辟,由系统在调用构造函数时自动完成。
  2. 调用基类的构造函数完成基类成员的初始化。
  3. 若派生类中含对象成员、const成员或引用成员,则必须在初始化列表中完成其初始化。
  4. 派生类构造函数体执行。
      以下代码的输出结果是什么?
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中关系:

  • 重载(overload)
    函数的参数类型或数目有所不同。重载与成员函数是否是虚函数无关,例如下面4个函数便是重载关系。
class A{
	...
	virtual int fun();
	void fun(int);
	void fun(double, double);
	static int fun(char);
}

成员函数重载的特征:

  1. 相同的函数范围(在同一个类中);
  2. 相同的函数名字;
  3. 不同的参数列表;
  4. virtual关键字可有可无。
  • 覆盖(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)两个函数参数不同,无论基类函数是否是虚函数,基类函数都会被屏蔽。和重载的区别在于两个函数不在同一类中。

你可能感兴趣的:(C/C++,类,成员函数,重载,覆盖,隐藏)