类的继承重难点

继承权限:

不同的继承方式会影响基类成员在派生类中的访问权限。
 
public继承方式
基类中所有 public 成员在派生类中为 public 属性;
基类中所有 protected 成员在派生类中为 protected 属性;
基类中所有 private 成员在派生类中不能使用。
 

protected继承方式
基类中的所有 public 成员在派生类中为 protected 属性;
基类中的所有 protected 成员在派生类中为 protected 属性;
基类中的所有 private 成员在派生类中不能使用。
 
private继承方式
基类中的所有 public 成员在派生类中均为 private 属性;
基类中的所有 protected 成员在派生类中均为 private 属性;
基类中的所有 private 成员在派生类中不能使用。

通过上面的分析可以发现:
1) 基类成员在派生类中的访问权限不得高于继承方式中指定的权限。例如,当继承方式为 protected 时,那么基类成员在派生类中的访问权限最高也为 protected,高于 protected 的会降级为 protected,但低于 protected 不会升级。再如,当继承方式为 public 时,那么基类成员在派生类中的访问权限将保持不变。
 
也就是说,继承方式中的 public、protected、private 是用来指明基类成员在派生类中的最高访问权限的。
 
2) 不管继承方式如何,基类中的 private 成员在派生类中始终不能使用(不能在派生类的成员函数中访问或调用)。
 
3) 如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为 public 或 protected;只有那些不希望在派生类中使用的成员才声明为 private。
 
4) 如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected。
 
注意,我们这里说的是基类的 private 成员不能在派生类中使用,并没有说基类的 private 成员不能被继承。实际上,基类的 private 成员是能够被继承的,并且(成员变量)会占用派生类对象的内存,它只是在派生类中不可见,导致无法使用罢了。private 成员的这种特性,能够很好的对派生类隐藏基类的实现,以体现面向对象的封装性。

#include <cstdlib>
#include <iostream>

using namespace std;

class A
{
private:
    int a;
protected:
    int b;
public:
    int c;

    A()
    {
        a = 0;
        b = 0;
        c = 0;
    }

    void set(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }
};

class B : public A
{
public:
    void print()
    {
        //cout<<"a = "<<a; //err
        cout<<"b = "<<b;
        cout<<"  c = "<< c << endl;
    }
};

class C : protected A
{
public:
    void print()
    {
        //cout<<"a = "<<a; //err
        cout<<"b = "<<b;
        cout<<"  c = "<< c << endl;
    }
};

class D : private A
{
public:
    void print()
    {
        //cout<<"a = "<<a; //err
        cout<<"b = "<<b;
        cout<<"  c = "<<c<<endl;
    }
};

int main()
{
    A aa;
    B bb;
    C cc;
    D dd;

    aa.c = 100; //ok
    bb.c = 100; //ok
    //cc.c = 100; //err 类的外部是什么含义
    //dd.c = 100; //err

    aa.set(1, 2, 3);
    bb.set(10, 20, 30);
    //cc.set(40, 50, 60); //ee
    //dd.set(70, 80, 90); //ee

    bb.print();
    cc.print();
    dd.print();

    
    return 0;
}

继承中构造和析构函数的规则:

1、子类对象在创建时会首先调用父类的构造函数。
2、父类构造函数执行结束后,执行子类的构造函数。
3、当父类的构造函数有参数时,需要在子类的初始化列表中显示调用。
4、析构函数调用的先后顺序与构造函数相反。

例:1.建立一个形状类Shape作为基类,派生出圆类Circle和矩形类Rectangle,求出面积并获取相关信息。具体要求如下:
(1)形状类Shape
(a)保护数据成员
double x,y:对于不同的形状,x和y表示不同的含义,如对于圆,x和y均表示圆的半径,而对于矩形,x表示矩形的长,y表示矩形的宽。访问权限定义为保护类型是为了能被继承下去,以便派生类能直接访问x和y。
(b)公有成员函数
构造函数Shape(double _x,double _y):用_x、_y分别初始化x、y。
double GetArea():求面积,在此返回0.0。
(2)圆类Circle,从Shape公有派生
(a)公有成员函数
Circle(double r):构造函数,并用r构造基类的x和y。
double GetArea():求圆的面积。
double GetRadius():获取圆的半径。
(3)矩形类Rectangle,从Shape公有派生
(a)公有成员函数
Rectangle(double l,double w) :构造函数,并用l和w构造基类的x和y。
double GetArea():求矩形的面积。
double GetLength():获取矩形的长。
double GetWidth():获取矩形的宽。
(4)在主函数中对派生类进行测试。注意,在程序的开头定义符号常量PI的值为3.14。测试的输出结果如下:
circle:r=1, area=3.14
rectangle:length=3, width=4, area=12。

#include <iostream>
#define PI 3.14
using namespace std;

class Shape
{
protected:
    double x,y;
public:
    Shape(double _x, double _y);
	double GetArea();
};

Shape::Shape(double _x, double _y)
{
	x = _x;
    y = _y;	
}

double Shape::GetArea()
{
	return 0.0;
}

class Circle : public Shape
{
public:
    Circle(double r);
	double GetArea();
	double GetRadius();
};

Circle::Circle(double r) : Shape(r, r)
{
	x = r;
	y = r;
}

double Circle::GetArea()
{
	return PI * x * y;
}

double Circle::GetRadius()
{
	return x;
}

class Rectangle : public Shape
{
public:
    Rectangle(double l, double w);
	double GetArea();
	double GetLength();
	double GetWidth();
};

Rectangle::Rectangle(double l, double w) : Shape(l, w)
{
	x = l;
	y = w;
}

double Rectangle::GetArea()
{
	return 2 * x * y;
}

double Rectangle::GetLength()
{
	return x;
}

double Rectangle::GetWidth()
{
	return y;
}

int main()
{
    Circle c(1.0);
	//c.Shape(1.0, 1.0);
    Rectangle r(3.0,4.0);
	//r.Shape(3.0, 4.0);
	cout << "r = " <<c.GetRadius() << " " <<"s = " << c.GetArea() << endl;
    cout << "l = " << r.GetLength() << " " << "w = " << r.GetWidth() << " " << " s = " << r.GetArea() << endl;
	return 0;
}

继承和组合混搭的时候构造函数和析构函数的规则:

1.原则:   先构造父类,再构造成员变量、最后构造自己
                 先析构自己,在析构成员变量、最后析构父类
                //先构造的对象,后释放

#include <iostream>
using namespace std;
 
class Object
{
public:
    Object(int a)
    {
        this->a = a;
        cout << "Object 构造函数 : "  << a <<  endl;
    }
 
    ~Object()
    {
        cout << "Object 析构函数: " << this->a << endl;
    }
private:
    int a;
};
 
class Parent
{
public:
    Parent(int a, int b)
    {
        cout << "parent 构造函数" << endl;
    }
    ~Parent()
    {
        cout << "parent 析构函数" << endl;
    }
private:
    int a;
    int b;
};

class Child : public Parent
{
public:
    Child(int c):obj1(1), Parent(2, 3),  obj2(2)
    {
        cout << "child 构造函数" << endl;
    }
    ~Child()
    {
        cout << "child 析构函数" << endl;
    }
 
private:
    int c;
    Object obj1;
    Object obj2;
};
 
int main()
{
    {
        Child ch(4);
    }
 
    return 0;
}

派生类中的static关键字:

基类定义的静态成员,将被所有派生类共享
根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质 (遵守派生类的访问控制)
派生类中访问静态成员,用以下形式显式说明:

 类名 :: 成员。
 或通过对象访问   对象名 . 成员 。

注意要给static 后面的东西分配内存。

多继承的派生类的构造和访问:

多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员
执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。
 一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员,派生类对象访问时应该加以识别。

虚继承

如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。
如果在多条继承路径上有一个公共的基类,那么在继承路径的某处汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象。
要使这个公共基类在派生类中只产生一个子对象,必须对这个基类声明为虚继承,使这个基类成为虚基类。
虚继承声明使用关键字       virtual
 


 

猜你喜欢

转载自blog.csdn.net/florence_/article/details/81268558