继承——基本语法
class 子类(派生类):继承方式 父类(基类) 继承方式: 1.public 公有继承 2.protected 保护继承 3.private 私有继承
下面来理解一下这张图片: 首先声明一点:无论是哪一种继承方式,子类都无法访问到父类的私有成员!!! 1.公有继承:父类中的public权限成员在子类中仍然为public权限成员。 父类中的protected权限成员在子类中仍然为protected权限成员。 且子类在类外访问不到protected权限内容。 2.保护继承:父类中的public与protected权限成员在子类中都变成了protected权限成员,在类外访问不到。 3。私有继承:父类中的public和protected权限成员都变成了private成员,在类外都不可访问。
继承中的对象模型
从父类中继承过来的成员,那些属于子类成员???
父类中所有非静态成员属性都会被子类继承,而父类中的所有私有成员属性是被编译器给隐藏了,因此访问不到,但确实是被继承下来了.
下面我们来看一个案例:
在这里插入代码片
```#include<iostream>
using namespace std;
class Base
{
public:
int m_A = 10;
protected:
int m_B = 20;
private:
int m_C = 30;
};
class Son : public Base
{
public:
int m_D = 40;
};
void test01()
{
Son s;
cout<<"s所占内存空间的大小 = "<<sizeof(s)<<endl;
}
int main()
{
test01();
return 0;
}
下面我们来看一下结果: 结果显示所占内存空间为:16 所以很显然父类的所有成员都被继承下来了。
构造和析构的顺序
下面我们来看一个案例:
在这里插入代码片
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout<<"Base的构造函数"<<endl;
}
~Base()
{
cout<<"Base的析构函数"<<endl;
}
};
class Son : public Base
{
public:
Son()
{
cout<<"Son的构造函数"<<endl;
}
~Son()
{
cout<<"Son的析构函数"<<endl;
}
};
void test01()
{
Son s;
}
int main()
{
test01();
return 0;
}
我们再来看一下结果: 可以看到新进行父类的构造函数,在进行子类的构造函数农户。 构造函数与析构函数的顺序相反:先构造的后析构,后构造的先析构。
同名成员处理
当子类中出现与父类一样的同名成员是,怎样通过子类对象,来访问子类或父类的同名成员。
1.如果是访问子类同名成员,则直接访问; 2.如果是访问父类同名成员,则加上作用域;
下面我们来看一下代码;
在这里插入代码片
#include<iostream>
using namespace std;
class Base
{
public:
int m_A = 10;
void func()
{
cout<<"调用Base的函数"<<endl;
}
};
class Son : public Base
{
public:
int m_A = 20;
void func()
{
cout<<"调用Son的函数"<<endl;
}
};
void test01()
{
Son s;
s.Base::func();
cout<<"Base的m_A = "<<s.Base::m_A<<endl;
s.func();
cout<<"Son的s.m_A = "<<s.m_A<<endl;
}
int main()
{
test01();
return 0;
}
图片中的父类和子类含有同名函数和同名数据成员 子类则直接调用,如下 父类则需要加上作用域,如下 好,接下来我们看一下上面代码的运行结果: 如果子类与父类有同名成员,则子类的同名成员会将父类的隐藏,如若想进行访问,就需要加上作用域;
同名静态成员处理
首先我们来看一下 静态成员变量属性: 1.所有对象都共享同一份数据; 2.编译阶段就分配内存; 3.类内声明,类外初始化; 静态成员函数属性: 1.只能访问静态成员变量,不能访问非静态成员变量; 2.所有对象都共享同一份;
与非静态同名成员处理方式一样,也是要加上作用域才能访问父类同名函数成员; 请看下面代码:
在这里插入代码片
#include<iostream>
using namespace std;
class Base
{
public:
static int m_A ;
static void func()
{
cout<<"调用Base的静态成员函数"<<endl;
cout<<"Base的m_A = "<<m_A<<endl;
}
};
int Base:: m_A = 10;
class Son : public Base
{
public:
static int m_A ;
static void func()
{
cout<<"调用Son的静态成员函数"<<endl;
cout<<"Son的m_A = "<<m_A<<endl;
}
};
int Son:: m_A = 20;
void test01()
{
Son s;
s.func();
s.Base::func();
}
int main()
{
test01();
return 0;
}
结果: 可以看到即使是静态同名函数成员,在访问子类同名成员时直接利用对象直接访问即可,而父类需要加上作用域才能够访问。 我们不光能够使用类对象进行访问,同时我们也可以使用类名进行访问静态同名函数成员,这一点就体现静态与非静态的出入;
在这里插入代码片
#include<iostream>
using namespace std;
class Base
{
public:
static int m_A ;
static void func()
{
cout<<"调用Base的静态成员函数"<<endl;
cout<<"Base的m_A = "<<m_A<<endl;
}
};
int Base:: m_A = 10;
class Son : public Base
{
public:
static int m_A ;
static void func()
{
cout<<"调用Son的静态成员函数"<<endl;
cout<<"Son的m_A = "<<m_A<<endl;
}
};
int Son:: m_A = 20;
void test01()
{
Son::func();
Son::Base::func();
}
int main()
{
test01();
return 0;
}
差别就在于: 其中子类类名直接加作用域比较好理解; 而访问父类中:Son::表示通过类名方式进行访问;Base::表示作用域; 总结来说:Son::Base::func(); 调用父类中的同名func()函数;
多继承语法
class 子类名 : 继承方式 父类1,继承方式 父类2 … 下面看一段代码:
在这里插入代码片
#include<iostream>
using namespace std;
class Base1
{
public:
int m_A = 10;
};
class Base2
{
public :
int m_B = 20;
};
class Son : public Base1,public Base2
{
public:
int m_C = 30;
int m_D = 40;
};
void test01()
{
cout<<"Son类所占内存大小:"<<sizeof(Son)<<endl;
}
int main()
{
test01();
return 0;
}
结果: 其中Son所占字节为 16 ,说明继承了Base1和Base2的数据成员。 但是多继承同时也会带来麻烦,所以实际运用中分不推荐使用,例如 两个父类中都含有m_A,那么子类在继承两个父类时,便会出现二义性,不知道该使用哪一个。 若想访问,则需要加上作用域
在这里插入代码片
void test01()
{
Son s;
cout<<"Base1中的m_A = "<<s.Base1::m_A<<endl;
cout<<"Base2中的m_A = "<<s.Base2::m_A<<endl;
}
这样便能够访问到Base1和Base2的m_A;
菱形继承
菱形继承也可以成为钻石继承
在这里插入代码片
#include<iostream>
using namespace std;
class Base
{
public:
int m_Age = 10;
};
class A:virtual public Base{};
class B:virtual public Base{};
class D:public A,public B
{
};
void test01()
{
D d;
cout<<"d.m_Age = "<<d.m_Age<<endl;
}
int main()
{
test01();
return 0;
}
因为类A和类B都会继承一份Base类中的m_Age,所以防止在类D多继承类A和类B时同时继承两份m_Age,而且这两份数据可能还不相等,所以在访问权限前加上virtual,使得类A和类B变成“虚基类”; 加上virtual后,其实在类A与类B中只是继承了vbptr指针,可以称为虚基类指针, (v:vi rtual , b:base, ptr:pointer) 虚基类指针指向Base类中的数据; 这样就能利用虚继承解决菱形继承的问题。 总结: 1.菱形继承带来的主要问题是子类继承相同的数据,导致资源量费毫无意义。 2.利用虚继承来解决菱形继承的问题。
以上就是c++中的一些继承问题。
|