菜鸟教程 -- 学的不仅是技术,更是梦想!

C++ 教程
C++ 教程 C++ 简介 C++ 环境设置 C++ 基本语法 C++ 注释 C++ 数据类型 C++ 变量类型 C++ 变量作用域 C++ 常量 C++ 修饰符类型 C++ 存储类 C++ 运算符 C++ 循环 C++ 判断 C++ 函数 C++ 数字 C++ 数组 C++ 字符串 C++ 指针 C++ 引用 C++ 日期 & 时间 C++ 基本的输入输出 C++ 结构体(struct) C++ vector 容器 C++ 数据结构

C++ 面向对象

C++ 类 & 对象 C++ 继承 C++ 重载运算符和重载函数 C++ 多态 C++ 数据抽象 C++ 数据封装 C++ 接口(抽象类)

C++ 高级教程

C++ 文件和流 C++ 异常处理 C++ 动态内存 C++ 命名空间 C++ 模板 C++ 预处理器 C++ 信号处理 C++ 多线程 C++ Web 编程

C++ 资源库

C++ STL 教程 C++ 导入标准库 C++ 标准库 C++ 有用的资源 C++ 实例 C++ 测验 C++ <iostream> C++ <fstream> C++ <sstream> C++ <iomanip> C++ <array> C++ <vector> C++ <list> C++ <forward_list> C++ <deque> C++ <stack> C++ <queue> C++ <priority_queue> C++ <set> C++ <unordered_set> C++ <map> C++ <unordered_map> C++ <bitset> C++ <algorithm> C++ <iterator> C++ <functional> C++ <numeric> C++ <complex> C++ <valarray> C++ <cmath> C++ <string> C++ <regex> C++ <ctime> C++ <chrono> C++ <thread> C++ <mutex> C++ <condition_variable> C++ <future> C++ <atomic> C++ <type_traits> C++ <typeinfo> C++ <exception> C++ <stdexcept> C++ <cstdio> C++ <cstdint> C++ <memory> C++ <new> C++ <utility> C++ <random> C++ <locale> C++ <codecvt> C++ <cassert> C++ <cwchar> C++ <climits> C++ <cfloat> C++ <cstdlib> C++ <numbers> C++ OpenCV
(追記) (追記ここまで)

C++ 继承

面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。

当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类

继承代表了 is a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。

代码如下:

// 基类
class Animal {
// eat() 函数
// sleep() 函数
};


//派生类
class Dog : public Animal {
// bark() 函数
};

基类 & 派生类

一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:

class derived-class: access-specifier base-class

其中,访问修饰符 access-specifier 是 public、protectedprivate 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。

假设有一个基类 Shape,Rectangle 是它的派生类,如下所示:

实例

#include<iostream>usingnamespacestd; // 基类classShape{public: voidsetWidth(intw){width = w; }voidsetHeight(inth){height = h; }protected: intwidth; intheight; }; // 派生类classRectangle: publicShape{public: intgetArea(){return(width * height); }}; intmain(void){RectangleRect; Rect.setWidth(5); Rect.setHeight(7); // 输出对象的面积cout << "Total area: " << Rect.getArea() << endl; return0; }

当上面的代码被编译和执行时,它会产生下列结果:

Total area: 35

访问控制和继承

派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。

我们可以根据访问权限总结出不同的访问类型,如下所示:

访问publicprotectedprivate
同一个类yesyesyes
派生类yesyesno
外部的类yesnono

一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数。
  • 基类的重载运算符。
  • 基类的友元函数。

继承类型

当一个类派生自基类,该基类可以被继承为 public、protected private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。

我们几乎不使用 protected private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

  • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有保护成员来访问。
  • 保护继承(protected): 当一个类派生自保护基类时,基类的公有保护成员将成为派生类的保护成员。
  • 私有继承(private):当一个类派生自私有基类时,基类的公有保护成员将成为派生类的私有成员。

多继承

多继承即一个子类可以有多个父类,它继承了多个父类的特性。

C++ 类可以从多个类继承成员,语法如下:

class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};

其中,访问修饰符继承方式是 public、protectedprivate 其中的一个,用来修饰每个基类,各个基类之间用逗号分隔,如上所示。现在让我们一起看看下面的实例:

实例

#include<iostream>usingnamespacestd; // 基类 ShapeclassShape{public: voidsetWidth(intw){width = w; }voidsetHeight(inth){height = h; }protected: intwidth; intheight; }; // 基类 PaintCostclassPaintCost{public: intgetCost(intarea){returnarea * 70; }}; // 派生类classRectangle: publicShape, publicPaintCost{public: intgetArea(){return(width * height); }}; intmain(void){RectangleRect; intarea; Rect.setWidth(5); Rect.setHeight(7); area = Rect.getArea(); // 输出对象的面积cout << "Total area: " << Rect.getArea() << endl; // 输出总花费cout << "Total paint cost: $" << Rect.getCost(area) << endl; return0; }

当上面的代码被编译和执行时,它会产生下列结果:

Total area: 35
Total paint cost: 2450ドル
AI 思考中...

9 篇笔记 写笔记

  1. #0

    孙程耿

    951***[email protected]

    1290

    另外多继承(环状继承),A->D, B->D, C->(A,B),例如:

    class D{......};
    class B: public D{......};
    class A: public D{......};
    class C: public B, public A{.....};

    这个继承会使D创建两个对象,要解决上面问题就要用虚拟继承格式

    格式:class 类名: virtual 继承方式 父类名

    class D{......};
    class B: virtual public D{......};
    class A: virtual public D{......};
    class C: public B, public A{.....};

    虚继承--(在创建对象的时候会创建一个虚表)在创建父类对象的时候

    A:virtual public D
    B:virtual public D

    实例:

    #include <iostream>
    using namespace std;
    //基类
    class D
    {
    public:
     D(){cout<<"D()"<<endl;}
     ~D(){cout<<"~D()"<<endl;}
    protected:
     int d;
    };
    class B:virtual public D
    {
    public:
     B(){cout<<"B()"<<endl;}
     ~B(){cout<<"~B()"<<endl;}
    protected:
     int b;
    };
    class A:virtual public D
    {
    public:
     A(){cout<<"A()"<<endl;}
     ~A(){cout<<"~A()"<<endl;}
    protected:
     int a;
    };
    class C:public B, public A
    {
    public:
     C(){cout<<"C()"<<endl;}
     ~C(){cout<<"~C()"<<endl;}
    protected:
     int c;
    };
    int main()
    {
     cout << "Hello World!" << endl;
     C c; //D, B, A ,C
     cout<<sizeof(c)<<endl;
     return 0;
    }
    • 1、与类同名的函数是构造函数。
    • 2、~ 类名的是类的析构函数。

    孙程耿

    951***[email protected]

    9年前 (2017年08月15日)
  2. #0

    为什么子类的构造函数中会出现在初始化列表中呢?原因在于子类能够从基类继承的内容限制上。

    我们知道,一个派生类继承了所有的基类方法,但下列情况除外:

    • 基类的构造函数、析构函数和拷贝构造函数。
    • 基类的重载运算符。
    • 基类的友元函数。

    因此,我们不能够在子类的成员函数体中调用基类的构造函数来为成员变量进行初始化。例如这样子是不可以的

    #include <iostream>
     
    using namespace std;
     
    // 基类
    class Shape 
    {
     public:
     Shape(int w,int h)
     {
     width=w;
     height=h;
     }
     protected:
     int width;
     int height;
    };
     
    // 派生类
    class Rectangle: public Shape
    {
     public:
     Rectangle(int a,int b)
     {
     Shape(a,b);
     }
     
    };

    但我们可以把基类的构造函数放在子类构造函数的初始化列表上,以此实现调用基类的构造函数来为子类从基类继承的成员变量初始化。

    #include <iostream>
     
    using namespace std;
     
    // 基类
    class Shape 
    {
     public:
     Shape(int w,int h)
     {
     width=w;
     height=h;
     }
     protected:
     int width;
     int height;
    };
     
    // 派生类
    class Rectangle: public Shape
    {
     public:
     Rectangle(int a,int b):Shape(a,b)
     {
     
     }
    };
    4年前 (2022年02月22日)
  3. #0

    SunnyClear

    238***[email protected]

    31

    关于构造函数初始化列表的执行顺序进行补充:

    #include<iostream>
    using namespace std;
    class A
    {
    public:
     A()
     {
     cout << "call A()" << endl;
     }
    };
    class B :A
    {
    public:
     B(int val) : A(), value(val)
     {
     val = 0; // 重新赋值
     cout << "call B()" << endl;
     cout << val << endl; 
     }
    private:
     int value;
    };
    int main()
    {
     B b(10);
     return 0;
    }
    /*
    * 结果如下 
    * call A()
    * call B()
    * 0
    * 说明放在初始化列表的部分在构造函数之前执行
    */

    SunnyClear

    238***[email protected]

    4年前 (2022年05月23日)
  4. #0

    小鹤

    799***[email protected]

    35

    派生类在继承基类的成员变量时,会单独开辟一块内存保存基类的成员变量,因此派生类自己的成员变量即使和基类的成员变量重名,但是也不会引起冲突。如下代码:

    #include <iostream>
    using namespace std;
    //基类
    class A
    {
    public:
     A(){n = 0;};
     ~A(){};
     int getA(){ return n;};
     void setA(int t){ n = t;};
    private:
     int n;
    };
    //派生类
    class B :public A
    {
    public:
     B(){ n = 0;};
     ~B(){};
     int getB(){ return n;};
     void setB(int t){ n = t;};
    private:
     int n;
    };
    int main(int argc, char * argv[])
    {
     B b;
     b.setA(10); //设置基类的成员变量n
     cout<<"A::n "<<b.getA()<<endl; 
     cout<<"B::n "<<b.getB()<<endl; 
     b.setB(9); //设置派生类的成员变量n 
     cout<<"A::n "<<b.getA()<<endl; 
     cout<<"B::n "<<b.getB()<<endl; 
     return 0; 
    }
    

    结果如下:

    A::n 10
    B::n 0
    A::n 10
    B::n 9

    小鹤

    799***[email protected]

    4年前 (2022年08月23日)
  5. #0

    LiuJW0

    308***[email protected]

    22

    构造函数调用顺序:基类 > 成员类 > 派生类;

    多继承派生类: 基类构造顺序 依照 基类继承顺序调用

    类成员:依照 类成员对象 定义顺序 调用成员类构造函数

    #include <iostream>
    using namespace std;
    class Shape { // 基类 Shape
    public:
     Shape() {
     cout << "Shape" << endl;
     }
     ~Shape() {
     cout << "~Shape" << endl;
     }
    };
    class PaintCost { // 基类 PaintCost
    public:
     PaintCost() {
     cout << "PaintCost" << endl;
     }
     ~PaintCost() {
     cout << "~PaintCost" << endl;
     }
    };
    // 派生类
    class Rectangle : public Shape, public PaintCost //基类构造顺序 依照 继承顺序
    {
    public:
     Rectangle() :b(), a(), Shape(), PaintCost(){
     cout << "Rectangle" << endl;
     }
     ~Rectangle() {
     cout << "~Rectangle" << endl;
     }
     PaintCost b; // 类成员变量构造顺序 依照 变量定义顺序
     Shape a; 
    };
    int main(void)
    {
     Rectangle Rect;
     return 0;
    }

    结果:

    Shape
    PaintCost
    PaintCost
    Shape
    Rectangle
    ~Rectangle
    ~Shape
    ~PaintCost
    ~PaintCost
    ~Shape

    LiuJW0

    308***[email protected]

    4年前 (2022年10月12日)
  6. #0

    ShenyanWu

    208***[email protected]

    8

    补充二楼说法,派生类是会继承父类的构造方法的,但在继承构造方法时有一些注意事项,这在 C++11 以及之后的标准中得到了支持。具体情况取决于派生类是否定义了自己的构造方法以及父类构造方法的可访问性。

    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    using namespace std;
    class Base {
    public:
     Base() {
     num = 5;
     cout << "BaseConstructorCall:" << protectedMember << endl;
     }
     Base(int n) {
     num = n;
     cout << "BaseConstructorCallnum:" << protectedMember << endl;
     }
     ~Base() {
     cout << "BaseDestructorCall:" << protectedMember << endl;
     }
    protected:
     int protectedMember = 0;
     int num;
    };
    class Derived1 : public Base {
    public:
     using Base::Base; // 这行声明继承父类的构造方法
     void modifyMember(int newValue) {
     protectedMember = newValue; // 修改父类的成员
     cout << "Derived1:" << num << endl;
     }
    };
    class Derived2 : public Base {
    public:
     void modifyMember(int newValue) {
     protectedMember = newValue; // 修改同一个父类的成员
     cout << "Derived2:" << num << endl;
     }
    };
    int main() {
     Derived1 d1(88);
     Derived2 d2;
     d1.modifyMember(10);
     d2.modifyMember(20);
     return 0;
    }

    可以在输出中看到

    Derived:88

    主要是需要使用基类构造定义:

    using Base::Base; // 这行声明继承父类的构造方法

    ShenyanWu

    208***[email protected]

    3年前 (2023年08月30日)
  7. #0

    小凡

    392***[email protected]

    5

    多继承与初始化列表的结合使用:

    #include <iostream>
    using namespace std;
    class Value{
     public:
     Value(int x){
     cout << "开始构造Value" << endl; 
     value = x;
     }
     int getValue()
     {
     return value;
     }
     ~Value()
     {
     cout << "开始析构Value" << endl; 
     }
     private:
     int value;
    };
    class Num{
     public:
     Num(int y)
     {
     cout << "开始构造Num" << endl; 
     num = y;
     }
     int getNum()
     {
     return num;
     }
     ~Num()
     {
     cout << "开始析构Num" << endl; 
     } 
     private:
     int num;
    };
    class Test : public Value,public Num{
     public:
     Test(int x,int y):Value(x),Num(y)
     {
     cout << "开始构造Test" << endl; 
     }
     getAllInfo()
     {
     cout << "value:" << getValue() << endl;
     cout << "num:" << getNum() << endl;
     }
     ~Test()
     {
     cout << "开始析构Test" << endl; 
     } 
    };
    int main(){
     Test t(10,20);
     t.getAllInfo();
     return 0;
    }

    输出:

    开始构造Value
    开始构造Num
    开始构造Test
    value:10
    num:20
    开始析构Test
    开始析构Num
    开始析构Value

    小凡

    392***[email protected]

    3年前 (2023年11月08日)
  8. #0

    不甘心

    102***[email protected]

    3

    通过调用基类的公有成员函数来访问基类的私有成员变量:

    #include <iostream>
    using namespace std;
    class Base {
    private:
     int privateVar;
    public:
     Base() : privateVar(10) {}
     void accessPrivateVar() {
     cout << "Base privateVar: " << privateVar << endl;
     }
    };
    class Derived : public Base {
    public:
     void accessBasePrivateVar() {
     // 通过调用基类的公有成员函数来访问基类的私有成员变量
     accessPrivateVar();
     }
    };
    int main() {
     Derived d;
     d.accessBasePrivateVar(); // 输出 Base privateVar: 10
     return 0;
    }

    不甘心

    102***[email protected]

    3年前 (2023年11月27日)
  9. #0

    kebing

    222***[email protected]

    10

    以下是对ShenyanWu的笔记的补充,除了使用:

    using Base::Base; // 这行声明继承父类的构造方法
    void modifyMember(int newValue) {
     protectedMember = newValue; // 修改父类的成员
     cout << "Derived1:" << num << endl;
    }

    还可以直接使用列表调用基类的构造函数,即改为:

    class Derived1 : public Base {
    public:
     Derived1(int n):Base(n) {//通过列表调用父类的构造函数
     
     }
     void modifyMember(int newValue) {
     protectedMember = newValue; // 修改父类的成员
     cout << "Derived1:" << num << endl;
     }
    };

    kebing

    222***[email protected]

    2年前 (2024年02月26日)

点我分享笔记

  • 昵称 (必填)
  • 邮箱 (必填)
  • 引用地址

AltStyle によって変換されたページ (->オリジナル) /