怎么给网站做备份呢,拼多多网店怎么注册开店,wordpress邮件配置,河南网站建设价格1.继承规则
继承的本质是复用#xff0c;是结构上的继承而不是内容上的继承#xff0c;近似于在子类中声明了父类的成员变量。
#xff08;1#xff09;写法#xff1a;class student : public person 派生类#xff08;子类#xff09;#xff0c;继承方式是结构上的继承而不是内容上的继承近似于在子类中声明了父类的成员变量。
1写法class student : public person 派生类子类继承方式基类父类
它们都分public、protected、private三种但是含义并不相同。
2区分访问限定符和继承方式
访问限定符修饰的是在当前类里面是否可以访问而继承方式是指在继承的类里面的访问方式并且一个成员继承后的访问限定符是父类的访问限制符和子类继承方式两者间的较小权限。如果A有public成员a而以protectd继承因此子类成员的访问限制符是protected。
访问权限由大到小是publicprotectedprivateprotected和private在继承语法外没有区别。比如protected成员public继承后是protectedprotected成员private继承后是privatepublic成员public继承后是public这都非常容易理解。 #include iostream
using namespace std;class A
{
public:int _a 0;
protected:int _b 1;
private:int _c 2;
};class B : public A
{
public:void GetNum(){cout _a _b _d endl;}int _d 3;
};int main()
{B().GetNum();return 0;
} 但是要注意private成员无论以何种方式继承不管是public、protected还是private继承最终子类成员的访问权限都是private且这个private成员不能被子类使用包括访问修改等
看起来这个成员没有被继承但实际上子类包含该成员。我们可以从监视窗口和类的大小双重验证这个结论。 3在继承里还规定友元关系不能继承父类的友元不是子类的友元只有子类自己声明友元
4形象理解父的成员是protected意味着这些成员对外是个秘密但在自己家庭中不算个秘密但家庭中的每个人都有义务对外保守这个秘密即protected无论以何种方式继承对外都不可见。
如果父成员有个private意味着这个成员是自己的秘密不能给任何人说比如家里有个隐藏的地下室。但是在继承给自己孩子这个房子时孩子不知有这个地下室但这个地下室真实存在即private无论以何种方式继承对子对外均不可见但这个private成员真实存在。
父的朋友不是子的朋友即友元关系不能继承。
5protected和private区分
protected在继承以后才有意义。protected和private对外的功能都一样都是不可见它们区别在protected对整个继承体系开放前提没有子类以private继承它而private是只有该类能访问其子类也不能访问。 当中途被继承为private后对子该成员依然不可见。
6默认继承方式
我们也可以选择不写继承方式这个时候class默认私有继承struct默认以public继承注意这和默认访问限定符一样但这是两个概念。 2.继承的函数调用
我们先尝试解读下面的代码这能帮助我们初步理解继承函数调用的特征 #include iostream
using namespace std;class A
{
public:A(int a, int b, int c):_a(a),_b(b),_c(c){}void GetNum(){printf(%d %d %d\n, _a, _b, _c);}int _a 0;
protected:int _b 1;
private:int _c 2;
};class B : public A
{
public:B():A(7, 8, 9), _d(5){}void GetNum(){printf(%d\n, _d);}int _d 3;
};int main()
{B().A::GetNum();B().GetNum();return 0;
}
结果是
1构造函数
A作为父类它的构造函数正常写就是了我们重点关注B的构造函数。 我们知道构造函数实际构造的顺序是声明的顺序而非初始化列表的顺序。这里我们可以理解为B子类的第一个成员声明是匿名的A在初始化时要根据A的构造函数把A当作一个整体用匿名初始化对象的方式A(7,8,9)来处理不能直接对A的成员变量直接初始化继承的A对象和创建一个A对象一样都不会复制函数和static变量在需要时都是直接去A里面取即整个继承体系共享一套函数和static变量
2函数的隐藏
父 子 在继承体系中是允许出现同名函数的但是这并不构成函数重载因为函数重载的前提是必须在同一个作用域定义两个同名函数而这里很明显是在两个类域里定义的同名函数所以一定不构成重载而是构成函数的隐藏只需要同名就构成隐藏了要和后面的多态区分开。
如果构成了函数隐藏的话应该怎么区分调用这两个函数呢很多人会以为能像函数重载的调用那样根据参数匹配程度来调用但这个逻辑在继承里走不通万一我就想利用隐式类型转换调用子的成员函数但是参数却匹配了父的成员函数呢万一是我参数写错了导致匹配错误的情况呢这些问题都会导致歧义的发生。所以规定只要构成函数隐藏调用父类一定要指明类域调用当前类的不用指明如果不指明类域一律只按调用当前类的函数来处理就算此时调用匹配父类的函数参数。
我们这里还可以学到虽然我们不能直接访问父类的私有成员_c 9但是我们可以选择调用父类的非private和protected函数从而达到间接访问的目的。
3析构函数与构造函数
析构函数的理解相对复杂我们先看一下下面的代码这能帮助我们深入理解构造和析构函数 #include iostream
using namespace std;class A
{
public:~A(){delete _a;}int* _a new int(1);
};class B : public A
{
public:B():_b(_a){}~B(){A::~A();*_b 2;}int* _b;
};int main()
{B a;return 0;
}结果是报错接下来我会详细分析里面的代码 为什么写作A::~A(); 析构函数的名字会被统一处理为destructor()父子类的析构函数同名构成了函数隐藏因此想要调用父的析构函数就必须要指定类域这个底层细节需要我们记住。这个特殊处理的原因在多态我会提及。
为什么报错 由于在父类的析构函数调用后间接使用了父类的成员所以出现越界访问的情况。
我们还能发现B的构造函数借助了A的成员变量。这里要区分开的是在子类初始化父类时只能以父类为整体去调用它的构造函数如A(7,8,9)而本质上继承的只是它的成员变量在子类的构造函数中可以直接用父类的成员。
如此一来我们能发现子类的构造函数可能会依靠父类的成员变量若父类先析构那在子类需要用到父类成员变量时就可能会发生越界访问。所以规定父类的构造函数一定先于子类父类的析构一定晚于子类构造先父后子析构先子后父。
在实现层面上由于子类的第一个成员变量默认是父类A所以我们可以不用关心毕竟初始化顺序是按声明而不是初始化列表。但是我们要避免显式地调用父类的析构即A::~A()不要出现在我们的继承代码中。当子类的析构函数走完后会去自动调用父类的析构。同理如果没有显式写构造编译器也会首先自动调用父类的默认构造。