基类和派生类的成员访问

时间:2022-10-02 19:58:53

#include
using namespace std;
class Cbase
{
public:
    Cbase(int x = 0, int y = 0, int z = 0):a(x), b(y), c(z){};
    void display();
    void display(int a, int b);
    void set();
    virtual void eval();
    static int sint;
protected:
    int c;
private:
    int a;
    int b;
};

class Cderive:public Cbase
{
public:
    Cderive(string x = "", string y = "", int z = 0):a(x),b(y), Cbase(z,z,z){};
    //隐藏了Cbase::set()的可见性
    void set(string x, string y);
    using Cbase::set;
    virtual void eval();
    virtual void cmp(Cderive* pb);
protected:
private:
    int nc;
    string a;
    string b;
};

1. 派生类中继承到的基类成员可以直接访问pulic成员

就好像他是派生类的成员,

可以通过该类的一个对象访问,

Cderive der;
der.display();

也可以直接在成员函数里面访问。

void Cderive::set()
{
    display();
    a = "hello";
    b = " derived class";
}

2.例外:当基类成员名在派生类中被重用时

c++初学者常见的误解:希望基类和派生类的成员函数构成一个重载函数集

但是,试图在派生类中调用基类实例会导致一个编译时刻错误

Cderive der;
//Cbase::set()不可见
der.set();
虽然基类成员可以被直接访问,但他们仍然属于基类的域。一个名字的重载候选函数必须出现在同一个域中,因为在不同的类域中

所以可以出现相同原型的两个实例,但是这在同一个类域中将导致重复定义错误。

如果我们真想为基类和派生类的成员实例提供一个重载函数集合有两种方法:

1.在调用基类实例的派生类中写一个小的inline存根函数:

class Cderive:public Cbase
{
public:
    void set(string x, string y);
    void set(){Cbase::set();}
};

2.使用using声明:

class Cderive:public Cbase
{
public:
    void set(string x, string y);
    using Cbase::set;
};

using把基类中每一个被命名的成员都引入到派生类的域中(针对一个成员函数的using声明不能指定参数表,只能指定成员函数名。这意味着,如果函数在基类中被重载,则所有的重载实例都被加入到派生类的类域中,不能只增加基类的重载成员函数集中的一个实例)。

3.访问基类的protected成员的范围

void Cderive::cmp(Cbase* pb)
{
    //自己的Cbase子对象的protected成员
    int mine = c;
    //不能直接访问另一个独立的Cbase对象的protected成员的权力
    int his = pb->c;

    if (mine == his)
        cout << are="" the="" same="" endl="">    else
        cout << are="" different="" endl="">}

Cderive只能访问一个Cbase类对象的protected成员,他自己的Cbase子对象。

这种形式的成员访问限制不适用于自己类的其他对象。例如

void Cderive::cmp(Cderive* pd)
{
    //自己的Cbase子对象的protected成员
    int mine = c;
    //ok as well
    int his = pd->c;

    if (mine == his)
        cout << are="" the="" same="" endl="">    else
        cout << are="" different="" endl="">}

派生类可以直接访问该类其他对象protected基类成员,以及该类其他对象的protected和private成员。

4.基类指针访问派生类对象

Cbase *pb = new Cderive;
pb->eval();
如果一个基类指针调用在Cbase中定义的虚拟函数,则调用派生的Cderive类实例。

除了“在Cbase基类中被声明,并且在Cderive派生类中被改写”的虚拟函数之外,没有办法通过pb直接访问Cderive的成员。

1.如果Cbase和Cderive都声明了一个同名的非虚拟函数同名的数据成员,通过pb调用的总是Cbase的实例。

2.如果Cderive引入了一个在Cbase中不存在的虚拟函数,那么试图通过pd调用它将导致一个编译时刻错误。

3.试图通过pb访问Cderive的数据成员或非虚拟成员函数,也会产生一个编译时刻错误。

C++中基类指针只能访问在该类中被声明的数据成员和成员函数,包括虚拟成员函数,而与他实际指向的实际对象无关。把一个成员声明成虚拟的, 只推延了“在程序执行期间pd指向的实际类型,对于要调用的实例的解析过程”。

5.静态成员的继承访问

如果基类定义了一个静态数据成员sint,派生类不会创建第二个sint的实例,所有的派生类对象都引用这个相同的,单一的,共享的静态成员。不论从基类派生了多少类sint只存在一个实例,我们可以通过派生类对象用成员访问语法来访问他。

Cbase *pb = new Cderive;
Cderive *pd = new Cderive;
cout << pd-="">sint<pd->sint = 2048;
cout << pb-="">sint <;>;>

6.派生类访问基类的私有成员,基类必须显式的把派生类声明为一个friend

class Cbase
{
    friend class Cderive;
public:
};

现在Cderive不但可以访问他自己的基类子对象的私有成员,而且还可以访问所有Cbase对象的私有和protected的成员。

如果从Cderive派生出一个Cchild类,Cchild类不继承Cderive与Cbase的友元关系,友元关系没有被继承。派生类没有成为“向他的基类授权友元的类的”友元。

Cchild没有对Cbase的访问特权。如果需要特权访问,则Cbase必须显式的向他授权。