C++核心(面向对象)
类和对象
继承
接博文C++_继承(上)
5.继承同名成员处理方式
- 问题:当子类与父类出现同名成员,如何通过子类对象访问到子类或父类中同名的数据呢?
- 访问子类同名成员,直接访问即可
- 访问父类同名成员,需要加作用域
#include <iostream>
using namespace std;
class Base{
public:
Base()
{
m_A = 100;
}
void func()
{
cout<<"父类成员函数的调用!"<<endl;
}
void func(int a) //父类中出现函数重载
{
cout<<"父类成员函数的调用!"<<"结果为:"<<a<<endl;
}
int m_A;
};
class Son : public Base{
public:
Son()
{
m_A = 200;
}
void func()
{
cout<<"子类成员函数的调用!"<<endl;
}
int m_A;
};
void test01()
{
Son s;
cout<<"子类下的m_A为:"<<s.m_A<<endl; //200,当子类和父类有同名成员属性时,默认访问到的是子类中的成员属性
cout<<"父类下的m_A为:"<<s.Base::m_A<<endl; //100,访问父类中的成员属性,需要加作用域
}
void test02()
{
Son s1;
cout<<"子类下的func为:"<<endl;
s1.func(); //当子类和父类有同名成员函数时,默认访问到的是子类中的成员函数
cout<<"父类下的func为:"<<endl;
s1.Base::func(); //访问父类中的成员函数,需要加作用域
cout<<"父类下的func函数重载为:"<<endl;
s1.Base::func(1000); //1000
}
int main()
{
//test01();
test02();
return 0;
}
总结:
- 子类对象可以直接访问到子类中同名成员
- 子类对象加作用域可以访问到父类同名成员
- 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名成员函数
6.继承同名静态成员处理方式
- 问题:继承中同名的静态成员在子类对象上如何进行访问?
- 静态成员和非静态成员出现同名,处理方式一致
- 访问子类同名成员,直接访问即可
- 访问父类同名成员,需要加作用域
#include <iostream>
using namespace std;
class Base{
public:
static int m_A; //类内定义
static void func()
{
cout<<"父类静态成员函数的调用!"<<endl;
}
};
int Base::m_A = 100; //类外初始化
class Son : public Base{
public:
static int m_A;
static void func()
{
cout<<"子类静态成员函数的调用!"<<endl;
}
};
int Son::m_A = 200;
//通过对象访问方式
void test01()
{
Son s;
cout<<"通过对象访问:"<<endl;
cout<<s.m_A<<endl; //200
s.func(); //子类
cout<<s.Base::m_A<<endl; //100
s.Base::func(); //父类
}
//通过类名访问方式
void test02()
{
cout<<"通过类名访问:"<<endl;
cout<<Son::m_A<<endl; //200
Son::func(); //子类
cout<<Son::Base::m_A<<endl; //100
Son::Base::func(); //父类
}
int main()
{
test01();
test02();
return 0;
}
7.多继承语法
- C++允许一个类继承多个类
- 语法:class 子类 :继承方式 父类1,继承方式 父类2
- 多继承可能引发父类中同名成员的出现,需要加作用域区分
C++实际开发中不建议用多继承
#include <iostream>
using namespace std;
class Base1{
public:
Base1()
{
m_A = 100;
}
int m_A;
};
class Base2{
public:
Base2()
{
m_A = 200;
}
int m_A;
};
class Son : public Base1, public Base2{
public:
Son()
{
m_C = 300;
m_D = 400;
}
int m_C;
int m_D;
};
void test01()
{
Son s;
cout<<sizeof(s)<<endl; //16
}
void test02()
{
Son s1;
cout<<s1.Base1::m_A<<endl; //100
cout<<s1.Base2::m_A<<endl; //200
cout<<s1.m_C<<endl; //300
cout<<s1.m_D<<endl; //400
}
int main()
{
test01();
test02();
return 0;
}
总结:多继承中如果父类中出现了同名情况,子类使用时候要加作用域。
8.菱形继承
菱形继承的概念:
- 两个派生类继承同一个基类
- 又有某个类同时继承两个派生类
- 这种继承被称为菱形继承,或者钻石继承
典型菱形继承案例:
菱形继承问题:
- 羊继承了动物的数据,驼同样继承了动物的数据,当羊驼使用数据,就会产生二义性
- 羊驼本身继承自动物的数据,继承了两份,其实我们应该清楚,这份数据只需一份就可以
#include <iostream>
using namespace std;
class Animal{ //动物类
public:
int m_Age;
};
class Sheep : public Animal{ //羊类
};
class Tuo : public Animal{ //驼类
};
class SheepTuo : public Sheep, public Tuo{ //羊驼类
};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
//当菱形继承两个父类拥有相同数据,需加作用域来区分
cout<<st.Sheep::m_Age<<endl; //18
cout<<st.Tuo::m_Age<<endl; //28
//这份数据我们知道只有一份就可以,菱形继承导致数据有两份,资源浪费
}
int main()
{
test01();
return 0;
}
针对菱形继承的问题,利用虚继承来解决上述问题。
- 继承之前加上关键字virtual,变为虚继承
- Animal类为虚基类
#include <iostream>
using namespace std;
//虚基类
class Animal{ //动物类
public:
int m_Age;
};
//虚继承
class Sheep : virtual public Animal{ //羊类
};
class Tuo : virtual public Animal{ //驼类
};
class SheepTuo : public Sheep, public Tuo{ //羊驼类
};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
//当菱形继承两个父类拥有相同数据,需加作用域来区分
cout<<st.Sheep::m_Age<<endl; //28
cout<<st.Tuo::m_Age<<endl; //28
//这份数据我们知道只有一份就可以,菱形继承导致数据有两份,资源浪费
//此时子类对象只继承了一份数据,可以有一种新的访问方式
cout<<st.m_Age<<endl; //28,上述两个输出操作相当于给同一份数据赋值,数据共享
}
int main()
{
test01();
return 0;
}
总结:菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费,而且毫无意义。利用虚继承可以解决菱形继承的问题。