C++中级-继承
基本语法
#include <iostream> #include <string> using namespace std;//语法:子类类名:public 父类类名
class Animal
{
public:
Animal() {};
void walk(string args) {
cout << args <<" is walking" << endl;
}
};class Dog:public Animal
{
public:
Dog() {
name = "dog";
}
string name;
};class Cat:public Animal
{
public:
Cat() {
name = "cat";
}
string name;
};int main() {
Dog d;
d.walk(d.name);Cat c; c.walk(c.name); </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;
}
继承方式
#include <iostream> #include <string> using namespace std; //private。public。protected继承 /* 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。 继承方式/基类成员 public成员 protected成员 private成员 public继承 public protected 不可见 protected继承 protected protected 不可见 private继承 private private 不可见 *///1.public
class Dad {
public:
int a = 11;
protected:
int b = 22;
private:
int c = 33;
};class Son :public Dad{
public:
};class Son2 :protected Dad {
public:
};class Son3 :private Dad {
public:
};
int main() {</span><span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;
}
多继承
#include <iostream> #include <string> using namespace std;class GrandFather{
public:</span><span style="color: #0000ff;">int</span> gargs = <span style="color: #800080;">11</span><span style="color: #000000;">;
};
class Father:public GrandFather{
public:
int fargs = 22;
};class Son:public GrandFather,public Father{
public:</span><span style="color: #0000ff;">int</span> s1 = <span style="color: #800080;">5</span><span style="color: #000000;">; </span><span style="color: #0000ff;">int</span> s2 = <span style="color: #800080;">6</span><span style="color: #000000;">;
};
int main() {
Son son;
cout << sizeof(son) << endl;//20个字节
return 0;
}
构造与析构顺序/属性继承访问
#include <iostream> using namespace std;//1.构造解析顺序;
//2.父类子类属性与方法同名访问办法;
//3.静态属性访问办法;class Base {
public:
Base() {cout << "Base in Base" << endl;};
~Base() { cout << "~Base in Base" << endl; }
int a = 20;
void func() { cout << "Base func" << endl; }
static int static_b;
};
int Base::static_b = 33;class Son:public Base {
public:
Son() { cout << "Son in Son" << endl; };
~Son() { cout << "~Son in Son" << endl; }
int a = 33;
void func() { cout << "Son func" << endl; }
static int static_b;};
int Son::static_b = 66;int main() {
Son s;
cout << s.a << endl;
cout << s.Base::a << endl;//2.1通过+作用域才能访问父类的属性。
s.func();
s.Base::func();//2.2通过+作用域才能访问父类的方法。</span><span style="color: #008000;">//</span><span style="color: #008000;">3.1通过对象访问</span> cout << s.static_b <<<span style="color: #000000;"> endl; cout </span><< s.Base::static_b <<<span style="color: #000000;"> endl; </span><span style="color: #008000;">//</span><span style="color: #008000;">3.2通过类名访问</span> cout << <span style="color: #800000;">"</span><span style="color: #800000;">''':</span><span style="color: #800000;">"</span><<Son::Base::static_b <<<span style="color: #000000;"> endl; cout </span><< <span style="color: #800000;">"</span><span style="color: #800000;">''':</span><span style="color: #800000;">"</span> << Son::static_b <<<span style="color: #000000;"> endl; </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;
}
/*
顺序:
Base in Base
Son in Son
~Son in Son
~Base in Base*/
#include <iostream>#include <string>using namespace std; //语法:子类类名:public 父类类名 class Animal{public:Animal() {};void walk(string args) {cout << args <<" is walking" << endl;}}; class Dog:public Animal{public:Dog() {name = "dog";}string name;}; class Cat:public Animal{public:Cat() {name = "cat";}string name;};int main() { Dog d;d.walk(d.name);
Cat c;c.walk(c.name);
return 0;}
虚继承
一、什么是二义性
在多继承的场景里,当父类中存在同名变量时,子类访问父类的同名变量,将出现二义性,因为编译器不知道你将要访问的是哪个父类中的变量。
举个例子:
- class A
- {
- public:
- int a; // B1,B2 都将继承一个变量 a
- };
- class B1 : public A
- {
- };
- class B2 : public A
- {
- };
- class C : public B1, public B2
- {
- };
-
- int main()
- {
- C c;
- c.a = 10; // ERROR ! 二义性 !!!
- return 0;
- }
二、怎么解决
1. 不使用多继承
一般来说,单继承就可以满足我们 99% 的需求了,我们应该尽量避免使用多继承带来的二义性问题。(注意:这里说的单继承不包括下面说的这种类似于 “接口” 的父类)。
由于 C++ 中不存在接口,但是可以使用只包含纯虚函数的抽象类替代,如果是只包含纯虚函数的抽象类,再多继承都将不会发生二义性(父类都没有变量了当然不会有二义性)。
2. 使用虚继承
虚继承只能解决多个父类的同名变量都是从公共基类中继承而来的情况,就是下图这种:
使用虚继承:
- class A
- {
- public:
- int a;
- };
- class B1 : virtual public A // 虚继承
- {
- };
- class B2 : virtual public A // 虚继承
- {
- };
- class C : public B1, public B2
- {
- };
-
- int main()
- {
- C c;
- c.a = 10; // OK,不会有二义性了
- return 0;
- }
原理:使用虚继承时,C++ 编译器会做特殊处理,只会调用一个公共基类 A 的构造方法,这样就不会创建出多个同名变量 a 了。
3. 使用 “类名::变量名” 显性访问
还有一种二义性出现的场景,就是多个父类都是独立的,它们没有公共基类,这些独立的父类中存在同名变量的话,就不能使用虚继承来解决了,类似下图这样子:
这种情况,我们就只能使用 “类名::变量名” 显性访问,避免二义性了:
- class B1
- {
- public:
- int a;
- };
- class B2
- {
- public:
- int a;
- };
- class C : public B1, public B2
- {
- };
-
- int main()
- {
- C c;
- c.B1::a = 10;
- c.B2::a = 20;
- return 0;
- }
同样,如果父类还存在同名方法,我们也可以使用 “类名::方法名” 这样显性调用。