当前位置:   article > 正文

【C++】继承/基类和派生类(赋值兼容性规则)/同名隐藏/切片问题_c++ 基类派生类成员重名

c++ 基类派生类成员重名

继承

:体现了由简单到复杂的过程。保持原有特性的基础进行扩展,增加功能。
通过继承对类进行分层,提供类型和派生类的关系。
也就是基类和派生类
struct A{} 当做一个数据集合来看待。只有数据
classB{}当做一个对象来看待。有数据和方法

保护属性,在继承关系中可以看做是共有属性。
无论采取何种继承方式,我们都可以访问基类的保护和公有。

1. struct和class区别:

  1. 在struct在设计类时,成员的属性默认为公有的,class默认为私有。
  2. struct设计的类叫做数据集合。而class设计的类叫做对象。数据集合代表着只需要有数据,而对象既有数据也有方法。
  3. 在继承概念中,struct的继承,在缺省访问限定符情况下,默认为公有继承。class的继承,在缺省访问限定符
    的情况下,默认为私有继承。
  4. 默认的继承方式和派生类有关。若派生类为class,基类为struct,则默认继承方式为私有继承,若派生类为struct,基类为class,默认为公有继承。
    示例:
//struct的公有继承。
struct A{}
structB:A{}
//class私有继承
classA{}
classB:A{}
//class继承struct
structA{}
classB:A  //私有继承。
//struct继承class
classA{}
structB:A{}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

1. 双层结构

1.1 若派生类公有继承了基类的公有成员问题。

下面的程序,能否说派生类继承基类的两个成员,即派生类现在有四个成员
这种说法肯定是错误的,不能根据设计的形式来谈论内存中的存在形式。
准确来说,派生类中有三个成员,分别是隐藏的基类对象c和d,这个隐藏的基类对象中包含了a,b两个成员。
如下:

class object
{
   publicint a;
     int b;
};
class Base:public Object
{
  public:
   int c;
   int d;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

1.2派生类方法访问隐藏的基类成员的问题

1.2.1公有方式继承:

示例:对于派生类的fun()函数,哪条语句不能通过编译通过?
请添加图片描述
很明显:ax编译不通过。
原因:由于fun()函数为派生类的公有方法,所以可以访问自己的成员,而继承下来的基类隐藏对象也属于派生类,所以可以访问隐藏对象的保护属性和公有属性。

1.2.2私有方式继承

请添加图片描述
很明显,ax还是不能编译。
原因:由于是私有方式继承下来,那么隐藏的的基类对象的权限和自身的private成员权限相同,既可以访问自己的私有成员,也可以访问隐藏基类对象的公有成员和保护成员。
隐藏基类对象的权限和bx是相同的。都属于private

  1. 对于隐藏的基类对象,可访问其保护属性和公有属性。
  2. 对于私有属性的基类对象,可以访问该对象的公有属性。
    可以说:在派生类中,基类对象是无名的,可以访问除私有属性外的成员,而作为私有属性的基类对象,是有名的,只可以访问共有属性。

    因此主要的区别就是,继承而来的对象和成员对象的差别就在于保护属性上

1.2.3继承对象和具名对象的区别:

派生类对象可以访问继承对象的保护和公有属性
具名对象为私有时,只能访问这个具名对象中的公有属性。
请添加图片描述

1.3 三层结构

1.3.1 B公有继承A类

class B;
class A
{
private:
    int ax;
protected:
    int ay;
public:
    int az;
};
class B :public A  //B可以访问A的公有和保护属性
{
private:
    int bx;
protected:
    int by;
public:
    int bz;
};
class C :public B //C可以访问B的公有和保护属性
{
private:
    int cx;
protected:
    int cy;
public:
    int cz;
    void fun()
    {
        cx = cy = cz = 10;
        bx = by = bz = 20;//访问不到bx
        ax = ay = az = 30;//访问不到ax
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

B公有继承A类,C类对象可以访问A类的公有和保护
B类对象对A对象的访问不论私有公有,都只能访问保护和公有

1.3.2 B私有继承A类

void fun()
    {
        cx = cy = cz = 10;
        bx = by = bz = 20;//访问不到bx,
        ax = ay = az = 30;//访问不到ax ,ay,az
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

B私有继承A类,C类对象访问不到A的任何一个属性
C类对象对B类的访问不论私有公有,都只能访问保护和公有

2. 派生类和基类

2.1 派生类的隐藏基类对象和基类作为成员变量的区别。

A aa;

class A
{
private:
    int ax;
protected:
    int ay;
public:
    int az;
    A() { ax = ay = az = 0; }

};
class B :public A
{
private:
    int bx;
    A aa;
protected:
    int by;
public:
    int bz;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

2.2 切片现象(赋值兼容性规则)

在公有继承中,派生类对象可以对基类对象进行赋值
请添加图片描述

比如:

class Object
{

private:
    int value;
public:
    Object(int x = 0) :value(x) { cout << "CreateObject:" << this << endl; }
    ~Object() { cout << "DestoryObject:" << this << endl; }
};
class Base :public Object
{
private:
    int num;
public:
    Base(int x = 0) :num(x),Object(x+10) { cout << "CreateBase:" << this << endl; }
    ~Base() { cout << "DestoryBase:" << this << endl; }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
class
int main()
{
    Object obja(100);
    Base base(10);
    obja = base;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
CreateObject:0073F6F8 //创建obja对象
CreateObject:0073F6E8 //创建隐藏基类对象
CreateBase:0073F6E8//创建base对象
DestoryBase:0073F6E8
DestoryObject:0073F6E8
DestoryObject:0073F6F8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

请添加图片描述

  • 创建隐藏基类对象
 Base(int x = 0) :num(x),Object(x+10)
 //创建隐藏基类对象
  • 1
  • 2

请添加图片描述

  • 隐藏基类对象中的value = 10+10= 20
    请添加图片描述
  • 隐藏基类对象初始化完,接着派生类base对象中num被初始化为10
 Base(int x = 0) :num(x),Object(x+10)
 //隐藏基类对象初始化完,接着派生类base对象中num被初始化为10
  • 1
  • 2

请添加图片描述

 obja = base;
  • 1
  • 将派生类base中的隐藏基类对象的value值赋给obja对象中value。
    请添加图片描述

2.2.1文字描述:main程序中的过程为:

  1. 先创建obja对象,对obja中value值进行初始化。
  2. 执行Base base(10)时,先创建隐藏基类对象,并对其value值初始化为20.
  3. 创建base对象,将base对象中的num进行初始化。
  4. 最后执行obja = base;是base中隐藏基类对象将其value赋值给obja对象中的value。
  5. 最后完成析构。

2.2.2画图理解:

请添加图片描述

2.2.3总结1:

请添加图片描述


2.3 派生类对象的地址给基类指针

int main()
{
    Object obja(100);
    Base base(10);
   // obja = base;
    Object* op = &base;
   // Object& obja = base; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

请添加图片描述

请添加图片描述

2.3.1总结:

请添加图片描述
引用其实就是基类引用派生类中隐藏基类对象中的值。

 如:Object& obja = base; ------》 obja是隐藏基类对象的别名。
  • 1

请添加图片描述

3. 同名隐藏:

3.1同名成员属性

当派生类中的成员属性和基类中的成员属性同名时,派生类中的成员属性会覆盖掉基类中的成员属性。

class B;
class A
{
protected:
    int ax;
public:
    A() :ax(0) {};
    ~A() {};
};
class B:public A
{
private:
    int ax;
public:
    B() :ax(10) {}
    void fun()
    {
        ax = 100;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
int main()
{
    B b;
    b.fun();
}
  • 1
  • 2
  • 3
  • 4
  • 5

先对隐藏基类对象中ax初始化为0,再对派生类B,b对象中ax初始化为10
请添加图片描述
调用b.fun() 只对派生类中的ax进行赋值,因为基类同名的成员属性被覆盖了。
请添加图片描述
当加上作用域,则就是给作用域内中的类的成员属性赋值。

 void fun()
    {
       // ax = 100; 
        A::ax = 100;
      //  B::ax = 200;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

请添加图片描述

3.2同名函数(不可以理解为重载,是在两个不同的类中)

class A
{
protected:
    int ax;
public:
    A() :ax(0) {};
    ~A() {};
    void fun() { cout << "A::fun()" << endl; }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
int main()
{
   B b;
   //b.fun();
   b.A::fun();//要访问必须基类公有方法,必须是公有继承。
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

外部函数在私有继承下,无法访问基类的任何成员不论公有,私有还是保护
只有在公有继承下,才可以访问基类的公有成员。

派生类如何直接访问隐藏基类对象的私有成员属性。直接将派生类设置为友元即可。

4. 派生类中使用拷贝构造

如果基类和派生类的拷贝构造函数都是这种形式:

Object(const Object& obj) : value(obj.value)
{
	cout << "Copy Create Object: " << this << endl;
}

Base(const Base& b) : num(b.num)
{
	cout << "Copy Create Base: " << this << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
int main()
{
    Base b1(10);
    Base b2(b1);  //
}
  • 1
  • 2
  • 3
  • 4
  • 5

在b2(b1)时,调用的基类对象的构造函数而不是拷贝构造函数
请添加图片描述
因此无法完成对b2中隐藏基类对象的的拷贝。需要改进

请添加图片描述

改进后:

对派生类拷贝构造函数改进后,就可以拷贝到隐藏基类对象中。


    //拷贝构造函数
    Object(const Object& obj):value(obj.value)
    {

        cout << "Copy Object:" << this << endl;
    }
    ~Object() { cout << "DestoryObject:" << this << endl; }
};

    //拷贝构造函数
    Base(const Base& b) :num(b.num), Object(b)
    {
        cout << "Copy Base:" << this << endl;
    }
    ~Base() { cout << "DestoryBase:" << this << endl; }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
int main()
{
    Base b1(10);
    Base b2(b1);  //
}
  • 1
  • 2
  • 3
  • 4
  • 5

请添加图片描述
请添加图片描述

  1. 隐藏基类对象调用其拷贝构造 value初始化
  2. 派生类对象再对num初始化

请添加图片描述
那么为什么 这样写: Base(const Base& b) :num(b.num), Object(b)。就可以完成对隐藏基类对象的赋值。
原因是因为,派生类对象可以对基类对象进行赋值,上面讲过,指针和引用是同样适用的。

错误写法:

千万不要在派生类的拷贝构造函数中调用基类的拷贝构造函数。
原因:调用这个基类对象,只会构造出来一个无名对象,和我们目标的隐藏基类对象无关,所以拷贝构造万就被析构了,没啥用。

Base(const Base& base) : num(base.num) //, Object(base)
	{
		Object::Object(base);
		cout << "Copy Create Base: " << this << endl;
	}
  • 1
  • 2
  • 3
  • 4
  • 5

请添加图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/106135
推荐阅读
相关标签
  

闽ICP备14008679号