做视频解析网站是犯法的么,wordpress自由定制导航,做网站如何挣钱,百度首页广告多少钱文章目录 继承的概念继承的定义方式继承关系和访问限定符基类和派生类对象的赋值转换继承中的作用域派生类中的默认成员函数构造函数拷贝构造函数赋值拷贝函数析构函数 总结 继承的概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段#xff0c;它允… 文章目录 继承的概念继承的定义方式继承关系和访问限定符基类和派生类对象的赋值转换继承中的作用域派生类中的默认成员函数构造函数拷贝构造函数赋值拷贝函数析构函数 总结 继承的概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段它允许程序员在保 持原有类特性的基础上进行扩展增加功能这样产生新的类称派生类。继承呈现了面向对象 程序设计的层次结构体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用继 承是类设计层次的复用。 继承中的一些概念 基类被一个类继承的类叫做基类。 派生类继承另一个类的类叫做派生类。
继承的使用场景当我们有很多个类时很多个类都有一些共同的特性有共同的成员变量或者成员函数时我们就可以把这些特性封装成一个公共的基类再通过这些类继承这个公共的基类如果个别类有独特的特性的话可以单独写在成员变量或者成员函数当中。 继承的定义方式
class A
{
private:public:int _a;
};
class B :public A
{
public:private:int _b;
};上面的代码是B类继承A类A类作为基类B类作为派生类
继承关系和访问限定符 继承方式有三种public继承private继承protected继承继承方式可以显示写也可以不写如果不写的话class的默认继承方式是privatestruct默认继承方式是public。 访问限定符有三种publicprivateprotected 这里有一个规则 : 基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 Min(成员在基类的访问限定符继承方式)public protectedprivate。大概规则就是在继承方式和访问限定符中取最小的一个如果访问限定符是public但是继承方式是private那么最后就取private就不能访问基类成员只要有private就不能访问基类的成员。 注意这里这是不能访问但是还是继承了 可以看到这里是继承了但是不能访问 如果对访问方式还是不清楚的可以看看上面的表格再看看上面的规则。 总结
基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私 有成员还是被继承到了派生类对象中但是语法上限制派生类对象不管在类里面还是类外面 都不能去访问它。基类private成员在派生类中是不能被访问如果基类成员不想在类外直接被访问但需要在 派生类中能访问就定义为protected。可以看出保护成员限定符是因继承才出现的。实际上面的表格我们进行一下总结会发现基类的私有成员在子类都是不可见。基类的其他 成员在子类的访问方式 Min(成员在基类的访问限定符继承方式)public protected private。使用关键字class时默认的继承方式是private使用struct时默认的继承方式是public不过 最好显示的写出继承方式。在实际运用中一般使用都是public继承几乎很少使用protetced/private继承也不提倡 使用protetced/private继承因为protetced/private继承下来的成员都只能在派生类的类里 面使用实际中扩展维护性不强
基类和派生类对象的赋值转换
class A
{
private:public:int _a;
};
class B :public A
{
public:B(){}
private:int _b;
};对于上面这个类来说我们做以下事情。
int main()
{B b;A a;a b;A ref b;A* Ptr b;return 0;
}这是不是类型转换呢很显然a和b不是一个类型如果我们只看赋值操作的话可能还会认为可能是类型转换但是如果我们看看下面的引用ref等于b的话就会打消这个念头了我们知道类型转换会产生临时变量临时变量具有常性所以这里引用不能引用一个具有常性的变量所以这里应该加const但是实践过程当中这里并没有报错所以这里肯定不是类型转换这里是一个特殊规则赋值兼容转换。 什么事赋值兼容转换呢 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片 或者切割。寓意把派生类中父类那部分切来赋值过去。 我们可以这样理解 赋值兼容转换可以理解为我们将一个派生类拷贝给一个基类实际上是把派生类中的基类部分拿出来单独给基类这里就像切片一样把派生类切开把基类的部分给基类所以这里才取名为切片或者切割。
int main()
{B b;A a;a b;A ref b;A* Ptr b;return 0;
}这里如果我们 改变ref对应的成员变量相应的b中的对应的继承下来的成员变量也会变指针也相同这里我们讨论的是public情况下如果是private就不能进行赋值兼容转换。
继承中的作用域
首先我们讨论一下基类和派生类中是否可以存在相同的函数或者变量答案是肯定的首先作用域不同可以存在相同的函数或者变量。
class A
{
public:void fun(){cout func() endl;}
};
class B : public A
{
public:void fun(int i){A::fun();cout func(int i)- iendl;}
};对于上面这两个类来说下面这个函数调用的是哪个fun呢?
void Test()B b;b.fun(10);
};这里很显然调用的是B中的fun()因为有参数但是下面这个呢
void Test()B b;b.fun();
};上面这个调用的哪个fun呢这里就不卖关子了这里会报错。 这里直接会报编译错误如果我们想调用A类中的函数怎么办呢 我们可以像下面一样调用。
void Test()
{B b;b.A::fun();
};像上面这样调用就可以直接调用A类中的相同函数。
这里我们提一下这两个同名函数的关系这两个同名函数构成隐藏。
派生类中的默认成员函数
构造函数
由于我们是先继承的基类的成员所以我们实例化的时候也是先定义的基类的成员所以在初始化的时候我们也是先初始化基类的成员变量 可以看到这里先初始化的是基类的成员即便我将基类的构造函数写在初始化列表的后面根据这个也可以断定基类的成员变量是在派生类原本的成员变量之前定义的因为初始化列表的初始化顺序是和声明的顺序一致的这说明在派生类中继承的基类的成员是先声明的这也说明应该先初始化基类成员再初始化派生类成员。
拷贝构造函数
class A
{
public:A(int a 0):_a(a){}A(const A a):_a(a._a){}
private:int _a;
};
class B : public A
{
public:B(int a 0, int b 0):_b(b),A(a){}B(const B b):_b(b._b),A(b){}
private:int _b;
};对于拷贝构造来说这里就要用到刚刚我们刚刚讲到的切片也就是赋值兼容转换如果是派生类的成员变量的话就直接走初始化列表直接进行初始化但是对于基类的成员这里我们直接调用基类的默认的拷贝构造函数这里我们直接传的变量是类型为B的这里会做一个切片直接将属于传递过去用的是A类接收所以这里会做一个切片传递过去只接受了A类的部分。
赋值拷贝函数 //A类的赋值拷贝函数A operator(const A a){_a a._a;}//B类的赋值拷贝函数B operator(const B b){_b b._b;A::operator(b);}
需要注意的是我们在复用基类的赋值拷贝函数的时候需要说明一下作用域因为两个赋值拷贝函数构成隐藏所以调用的时候会优先调用派生类的赋值拷贝函数所以这里如果不指定作用域的话会出现无穷递归很明显下面就出现了无穷递归所以这里要指定作用域。
析构函数
对于析构函数来说首先我们要知道的是在我们调用构造函数的时候先初始化的是基类的成员所以应该是基类成员先初始化然后再初始化派生类自身的成员所以这里应该是基类的成员先入栈派生类再入栈所以这里我们应该先保证先析构派生类的成员变量再析构基类的成员变量我们需要保证这一点由于C的机制C在析构完派生类的成员变量的时候会直接自动调用基类的析构函数所以我们并不用显示调用析构函数但是如果我们也可以显示调用。
这里是因为多态的某些原因后面细讲。 这里应该指定作用域所以应该像下面一样调用。 但是我们可以看见一个对象调用了两次析构而且还是先析构的是基类成员所以这里我们就不需要显示写出来直接等编译器自动调用即可。
总结
继承是面向对象编程中的一个核心概念通过它我们能够实现代码的重用和扩展。在本篇博客中我们从继承的基本概念开始逐步深入探讨了继承的定义方式、继承关系中的访问限定符以及基类和派生类对象之间的赋值转换。我们还讨论了继承中的作用域问题以及派生类中的默认成员函数包括构造函数、拷贝构造函数、赋值拷贝函数和析构函数。
特别地我们重点分析了析构函数在多态性中的作用。通过将基类的析构函数声明为虚函数确保在基类指针指向派生类对象时能够正确调用派生类的析构函数从而避免资源泄露和内存管理问题。
通过这些内容的学习我们不仅理解了继承的基本原理和实现方法还掌握了如何在实际编程中应用这些知识提高代码的可维护性和扩展性。希望这些内容能为大家提供有价值的参考帮助大家更好地理解和应用继承这一重要的编程概念。