• C++ 学习(13)类和对象 - 继承、继承方式、对象模型、构造与析构顺序、继承同名成员、多继承、菱形继承


    继承是面向对象的特性之一,可减少重复代码。

    1、继承的基本语法

    C++ 继承基本语法

    语法: class 子类: 继承方式 父类

    父类也称作基类,子类也称作派生类

    派生类(子类)成员包含两大部分:

    一类是从基类(父类)继承过来的,一类是自己增加的成员。从基类继承过来的表现其共性,而新增的成员体现其个性。 

    1. #include
    2. using namespace std;
    3. //基类(父类)
    4. class Base
    5. {
    6. public:
    7. void printBase()
    8. {
    9. cout << "父类成员函数" << endl;
    10. }
    11. };
    12. //派生类(子类)
    13. class Sub : public Base
    14. {
    15. public:
    16. void printSub()
    17. {
    18. cout << "子类成员函数" << endl;
    19. }
    20. };
    21. int main()
    22. {
    23. //类和对象 - 继承 - 基本语法
    24. Sub s1;
    25. s1.printSub();
    26. s1.printBase();
    27. system("pause");
    28. return 0;
    29. }

     输出结果

    子类成员函数
    父类成员函数

    Go语言 继承

    Go语言中没有class,继承的实现是通过在struct结构体内置匿名成员来实现的。

    1. package main
    2. import "fmt"
    3. //基类(父类)
    4. type Base struct {
    5. }
    6. func (base *Base) printBase() {
    7. fmt.Println("父类成员函数")
    8. }
    9. //派生类(子类)
    10. type Sub struct {
    11. Base //内置匿名父类结构体
    12. }
    13. func (sub *Sub) printSub() {
    14. fmt.Println("子类成员函数")
    15. }
    16. func main() {
    17. sub := Sub{}
    18. sub.printSub()
    19. sub.printBase()
    20. }

    输出结果

    子类成员函数
    父类成员函数

    2、继承方式

    C++ 继承方式

     三种继承方式:

    • public 公共继承:子类继承父类成员时,不能访问父类私有成员,父类中public成员继承后还是public,父类中protected成员继承后还是protected
    • protected 保护继承:子类继承父类成员时,不能访问父类私有成员,父类中public成员继承后变成protected,父类中protected成员继承后还是protected
    • private 私有继承:子类继承父类成员时,不能访问父类私有成员,父类中public成员与protected成员继承后变成private成员

     (1)公共继承(public)

    1. #include
    2. using namespace std;
    3. class Base
    4. {
    5. public:
    6. int a;
    7. protected:
    8. int b;
    9. private:
    10. int c;
    11. };
    12. //公有继承
    13. class Sub : public Base
    14. {
    15. public:
    16. void setVal()
    17. {
    18. a = 1;
    19. b = 2;
    20. //c = 3; //子类中不能访问父类的私有成员,报错:成员Base::c不可访问
    21. }
    22. };
    23. int main()
    24. {
    25. //类和对象 - 继承方式 - 公有继承
    26. //public 公共继承:子类继承父类成员时,不能访问父类私有成员,父类中public成员继承后还是public,父类中protected成员继承后还是protected
    27. Sub s1;
    28. s1.a;
    29. //s1.b; //子类对象不能访问父类的保护成员,报错:成员Base::b不可访问
    30. system("pause");
    31. return 0;
    32. }

     (2)保护继承(protected)

    1. #include
    2. using namespace std;
    3. class Base
    4. {
    5. public:
    6. int a;
    7. protected:
    8. int b;
    9. private:
    10. int c;
    11. };
    12. //保护继承
    13. class Sub : protected Base
    14. {
    15. public:
    16. void setVal()
    17. {
    18. a = 1; //在子类中,已变为保护成员,子类能够访问,子类对象不能访问
    19. b = 2;
    20. //c = 3; //子类中不能访问父类的私有成员,报错:成员Base::c不可访问
    21. }
    22. };
    23. //继承子类
    24. class Grand : public Sub
    25. {
    26. public:
    27. void setVal()
    28. {
    29. a = 3; //子类中可以访问父类的保护成员
    30. b = 5; //子类中可以访问父类的保护成员
    31. }
    32. };
    33. int main()
    34. {
    35. //类和对象 - 继承方式 - 保护继承
    36. //protected 保护继承:子类继承父类成员时,不能访问父类私有成员,父类中public成员继承后变成protected,父类中protected成员继承后还是protected
    37. Sub s1;
    38. //s1.a; //保护继承后,子类对象不能访问父类成员中的公有成员,因为在子类继承中已变成protected,报错:成员Base::a不可访问
    39. //s1.b; //对象不能访问保护成员,报错:成员Base::b不可访问
    40. Grand g1;
    41. //g1.a; //对象不能访问保护成员,报错:成员Base::b不可访问
    42. //g1.b; //对象不能访问保护成员,报错:成员Base::b不可访问
    43. system("pause");
    44. return 0;
    45. }

     (3)私有继承(private)

    1. #include
    2. using namespace std;
    3. class Base
    4. {
    5. public:
    6. int a;
    7. protected:
    8. int b;
    9. private:
    10. int c;
    11. };
    12. //私有继承
    13. class Sub : private Base
    14. {
    15. public:
    16. void setVal()
    17. {
    18. a = 1; //在子类中,已变为私有成员,不能子类访问
    19. b = 2; //在子类中,已变为私有成员,不能子类访问
    20. //c = 3; //子类中不能访问父类的私有成员,报错:成员Base::c不可访问
    21. }
    22. };
    23. //继承子类
    24. class Grand : public Sub
    25. {
    26. public:
    27. void setVal()
    28. {
    29. //a = 1; //子类中不能访问父类的私有成员,报错:成员Base::a不可访问
    30. }
    31. };
    32. int main()
    33. {
    34. //类和对象 - 继承方式 - 私有继承
    35. //private 私有继承:子类继承父类成员时,不能访问父类私有成员,父类中public成员与protected成员继承后变成private成员
    36. Sub s1;
    37. //s1.a; //私有继承后,子类对象不能访问父类成员中的公有成员,因为在子类继承中已变成private,报错:成员Base::a不可访问
    38. //s1.b; //私有继承后,子类对象不能访问父类的保护成员,因为在子类继承中已变成private,报错:成员Base::b不可访问
    39. system("pause");
    40. return 0;
    41. }

    Go语言 继承方式

    Go语言中,没有像C++有public、protected、private的修饰访问权限,是通过首字母大小写来确定访问权限的,方法名、常量、变量名、结构体名等,同包内首字母大小写都可访问(理解为公有的),不同包时只有首字母大写才能被访问(理解为公有的),不同包内首字母小写不能被访问(理解为私有的)。

    在base包中定义一个父类结构体

    1. package base
    2. //基类(父类) - 结构体名首字母大写
    3. type Base2 struct {
    4. Name string //首字母大写
    5. age int //首字母小写
    6. }
    7. //结构体名首字母小写
    8. type base3 struct {
    9. Name string //首字母大写
    10. age int //首字母小写
    11. }

    在main包中继承base包中的父类结构体

    1. package main
    2. import (
    3. "fmt"
    4. base "testProject/CPlus/2-类/继承方式"
    5. )
    6. //基类(父类)
    7. type Base struct {
    8. Name string //
    9. age int //
    10. }
    11. //派生类(子类) - 继承同包中的父类
    12. type Sub struct {
    13. Base //内置匿名父类结构体(同包内)
    14. }
    15. //派生类(子类) - 继承不同包中的父类 - 结构体名首字母大写
    16. type Sub2 struct {
    17. base.Base2 //内置匿名父类结构体(不同包)
    18. }
    19. //派生类(子类) - 继承不同包中的父类 - 结构体名首字母小写
    20. type Sub3 struct {
    21. // base.base3 //不能访问不同包下首字母小写的结构体,error: Unexported type "base3" usage
    22. }
    23. func main() {
    24. fmt.Println("---- 子类继承同包中的父类 ----")
    25. sub := Sub{}
    26. sub.Name = "Hello"
    27. sub.age = 2
    28. fmt.Printf("Name = %s, age = %d\n", sub.Name, sub.age)
    29. fmt.Println("---- 子类继承不同包中的父类 ----")
    30. sub2 := Sub2{}
    31. sub2.Name = "World"
    32. //sub2.age = 23 //error: Unexported field "age" usage
    33. fmt.Printf("Name = %s, 不能访问小写字母开头的属性 age\n", sub2.Name)
    34. }

    ---- 子类继承同包中的父类 ----
    Name = Hello, age = 2
    ---- 子类继承不同包中的父类 ----
    Name = World, 不能访问小写字母开头的属性 age

    3、对象模型

    C++ 内存对象模型

    1. #include
    2. using namespace std;
    3. class Base
    4. {
    5. public:
    6. int a;
    7. protected:
    8. int b;
    9. private:
    10. int c;
    11. };
    12. class Sub : public Base
    13. {
    14. public:
    15. int d;
    16. };
    17. int main()
    18. {
    19. //类和对象 - 继承 - 对象模型
    20. cout << "子类占用空间大小:" << sizeof(Sub) << endl;
    21. system("pause");
    22. return 0;
    23. }

    输出结果

    子类占用空间大小:16

    查看内存对象模型

    命令: cl /d1 reportSingleClassLayout类名 "文件名"

    reportSingleClassLayout后面连接的是要查看的类名

    注意:/d1 此处是数字1,不是小写字母l

    F:\CPlus\>cl /d1 reportSingleClassLayoutSub "rs.cpp"
    用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.32.31332 版
    版权所有(C) Microsoft Corporation。保留所有权利。

    rs.cpp

    class Sub       size(16):
            +---
     0      | +--- (base class Base)
     0      | | a
     4      | | b
     8      | | c
            | +---
    12      | d
            +---
    E:\Program\VisualStudio\VC\Tools\MSVC\14.32.31326\include\ostream(301): warning C4530: 使用了 C++ 异常处理程序,但未启用展开语义。请指定 /EHsc
    E:\Program\VisualStudio\VC\Tools\MSVC\14.32.31326\include\ostream(294): note: 在编译 类 模板 成员函数“std::basic_ostream> &std::basic_ostream>::operator <<(unsigned int)”时
    rs.cpp(23): note: 查看对正在编译的函数 模板 实例化“std::basic_ostream> &std::basic_ostream>::operator <<(unsigned int)”的引用
    rs.cpp(23): note: 查看对正在编译的 类 模板 实例化“std::basic_ostream>”的引用
    Microsoft (R) Incremental Linker Version 14.32.31332.0
    Copyright (C) Microsoft Corporation.  All rights reserved.

    /out:rs.exe
    rs.obj

    Go语言 子类占用空间大小

    1. package main
    2. import (
    3. "fmt"
    4. "unsafe"
    5. )
    6. //基类(父类)
    7. type Base struct {
    8. a int32
    9. b int32
    10. c int32
    11. }
    12. //派生类(子类)
    13. type Sub struct {
    14. Base
    15. d int32
    16. }
    17. func (s *Sub) print() {
    18. fmt.Println(s)
    19. }
    20. func main() {
    21. sub := Sub{}
    22. fmt.Printf("子类占用空间大小 : %d\n", unsafe.Sizeof(sub))
    23. }

     输出结果

    子类占用空间大小 : 16

    4、C++ 继承中构造和析构顺序

    • 构造函数顺序:父类先构造,子类再构造
    • 析构函数顺序:子类先析构,父类再析构;与构造函数顺序相反
    1. #include
    2. using namespace std;
    3. class Base
    4. {
    5. public:
    6. Base()
    7. {
    8. cout << "父类构造函数" << endl;
    9. }
    10. ~Base()
    11. {
    12. cout << "父类析构函数" << endl;
    13. }
    14. };
    15. class Sub : public Base
    16. {
    17. public:
    18. Sub()
    19. {
    20. cout << "子类构造函数~" << endl;
    21. }
    22. ~Sub()
    23. {
    24. cout << "子类析构函数~" << endl;
    25. }
    26. };
    27. void test()
    28. {
    29. Sub s1;
    30. }
    31. int main()
    32. {
    33. //类和对象 - 继承 - 构造函数与析构函数顺序
    34. test();
    35. system("pause");
    36. return 0;
    37. }

    父类构造函数
    子类构造函数~
    子类析构函数~
    父类析构函数

    5、继承同名成员处理方式

    子类与父类出现同名的成员时:

    • 访问子类同名成员,直接访问即可
    • 访问父类同名成员,需要加作用域

    C++ 继承同名成员处理方式

    子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数(成员函数重载时会有多个同名函数),如果子类想访问父类中被隐藏掉的同名成员函数,需要加作用域。 

    1. #include
    2. using namespace std;
    3. class Base
    4. {
    5. public:
    6. int a;
    7. Base() {
    8. a = 3;
    9. }
    10. void f1()
    11. {
    12. cout << "父类中同名成员函数f1()" << endl;
    13. }
    14. void f1(int _a)
    15. {
    16. cout << "父类中同名成员函数f1(int), 参数值 = " << _a << endl;
    17. }
    18. };
    19. class Sub : public Base
    20. {
    21. public:
    22. int a; //成员属性与父类同名
    23. Sub()
    24. {
    25. a = 5;
    26. }
    27. void f1() //成员函数与父类同名
    28. {
    29. cout << "子类中同名成员函数f1()" << endl;
    30. }
    31. };
    32. int main()
    33. {
    34. //类和对象 - 继承 - 继承同名成员处理方式
    35. /*
    36. 访问子类同名成员,直接访问即可
    37. 访问父类同名成员,需要加作用域
    38. */
    39. Sub s1;
    40. cout << "访问子类同名成员属性, a = " << s1.a << endl;
    41. cout << "访问父类同名成员属性, a = " << s1.Base::a << endl;
    42. s1.f1();
    43. s1.Base::f1();
    44. s1.Base::f1(8);
    45. system("pause");
    46. return 0;
    47. }

    输出结果 

    访问子类同名成员属性, a = 5
    访问父类同名成员属性, a = 3
    子类中同名成员函数f1()
    父类中同名成员函数f1()
    父类中同名成员函数f1(int), 参数值 = 8

    Go语言 继承同名成员处理方式 

    注:Go语言中没有重载函数

    1. package main
    2. import (
    3. "fmt"
    4. )
    5. //基类(父类)
    6. type Base struct {
    7. a int
    8. }
    9. func (base *Base) print() {
    10. fmt.Println("父类同名函数中,父类同名属性 a = ", base.a)
    11. }
    12. //派生类(子类)
    13. type Sub struct {
    14. Base //内置匿名父类结构体
    15. a int //成员属性与父类同名
    16. }
    17. //成员函数与父类同名
    18. func (sub *Sub) print() {
    19. fmt.Println("子类同名函数中,子类同名属性 a = ", sub.a)
    20. }
    21. func main() {
    22. fmt.Println("---- 子类成员与父类同名 ----")
    23. sub := Sub{}
    24. sub.a = 3
    25. sub.print()
    26. sub.Base.a = 5 //访问父类成员属性
    27. sub.Base.print() //访问父类成员函数
    28. }

    输出结果

     ---- 子类成员与父类同名 ----
    子类同名函数中,子类同名属性 a =  3
    父类同名函数中,父类同名属性 a =  5

    6、继承中静态成员处理方式

    C++ 继承中静态成员

    继承中同名的静态成员在子类对象中的访问方式,与非静态成员访问方式一样:

    • 访问子类同名成员,直接访问即可
    • 访问父类同名成员,需要加作用域

    可通过对象访问,也可通过类名访问

    1. #include
    2. using namespace std;
    3. class Base
    4. {
    5. public:
    6. static int a;
    7. static void f1()
    8. {
    9. cout << "父类中同名成员函数f1()" << endl;
    10. }
    11. static void f1(int _a)
    12. {
    13. cout << "父类中同名成员函数f1(int),参数值 = " << _a << endl;
    14. }
    15. };
    16. int Base::a = 5;
    17. class Sub : public Base
    18. {
    19. public:
    20. static int a; //成员属性与父类同名
    21. static void f1() //成员函数与父类同名
    22. {
    23. cout << "子类中同名成员函数f1()" << endl;
    24. }
    25. };
    26. int Sub::a = 3;
    27. int main()
    28. {
    29. //类和对象 - 继承 - 继承同名成员处理方式 - 静态成员
    30. /*
    31. 访问子类同名成员,直接访问即可
    32. 访问父类同名成员,需要加作用域
    33. */
    34. Sub s1;
    35. cout << "--- 通过对象访问 ---" << endl;
    36. cout << "访问子类同名成员属性, a = " << s1.a << endl;
    37. cout << "访问父类同名成员属性, a = " << s1.Base::a << endl;
    38. s1.f1();
    39. s1.Base::f1();
    40. s1.Base::f1(8);
    41. cout << endl << "--- 通过类名访问 ---" << endl;
    42. cout << "访问子类同名成员属性, a = " << Sub::a << endl;
    43. cout << "访问父类同名成员属性, a = " << Sub::Base::a << endl;
    44. Sub::f1();
    45. Sub::Base::f1();
    46. Sub::Base::f1(12);
    47. system("pause");
    48. return 0;
    49. }

    输出结果

    --- 通过对象访问 ---
    访问子类同名成员属性, a = 3
    访问父类同名成员属性, a = 5
    子类中同名成员函数f1()
    父类中同名成员函数f1()
    父类中同名成员函数f1(int),参数值 = 8

    --- 通过类名访问 ---
    访问子类同名成员属性, a = 3
    访问父类同名成员属性, a = 5
    子类中同名成员函数f1()
    父类中同名成员函数f1()
    父类中同名成员函数f1(int),参数值 = 12

    Go语言中没有静态成员

    7、多继承

    C++ 多继承

    C++ 允许一个类继承多个类,多继承可能会引发父类中有同名成员出现,需要加作用域区分。

    语法: class 子类: 继承方式 父类1,  继承方式 父类2 ......

    1. #include
    2. using namespace std;
    3. //父类
    4. class Base1
    5. {
    6. public:
    7. int a;
    8. int b;
    9. Base1()
    10. {
    11. a = 11;
    12. b = 12;
    13. }
    14. };
    15. //父类
    16. class Base2
    17. {
    18. public:
    19. int a; //同名成员属性
    20. int c;
    21. Base2()
    22. {
    23. a = 21;
    24. c = 22;
    25. }
    26. };
    27. //子类 - 多继承
    28. class Sub : public Base1, public Base2
    29. {
    30. public:
    31. int d;
    32. int e;
    33. Sub()
    34. {
    35. d = 31;
    36. e = 32;
    37. }
    38. };
    39. int main()
    40. {
    41. //类和对象 - 继承 - 多继承
    42. Sub s1;
    43. cout << "访问子类自己的成员属性: d = " << s1.d << endl;
    44. cout << "访问父类Base1的成员属性: b = " << s1.Base1::b << endl;
    45. cout << "访问父类Base2的成员属性: c = " << s1.Base2::c << endl;
    46. cout << "访问父类Base1的同名成员属性: a = " << s1.Base1::a << endl;
    47. cout << "访问父类Base2的同名成员属性: a = " << s1.Base2::a << endl;
    48. system("pause");
    49. return 0;
    50. }

    输出结果

    访问子类自己的成员属性: d = 31
    访问父类Base1的成员属性: b = 12
    访问父类Base2的成员属性: c = 22
    访问父类Base1的同名成员属性: a = 11
    访问父类Base2的同名成员属性: a = 21

    Go语言多继承

    Go语言可以通过匿名组合方式实现多继承(组合继承)。

    1. package main
    2. //基类(父类)
    3. type Base1 struct {
    4. a int
    5. b int
    6. }
    7. //基类(父类)
    8. type Base2 struct {
    9. a int //同名成员属性
    10. c int
    11. }
    12. //派生类(子类)
    13. type Sub struct {
    14. Base1 //内置匿名父类结构体
    15. Base2
    16. d int
    17. }
    18. func main() {
    19. sub := Sub{}
    20. sub.d = 31 //访问子类自己的成员属性
    21. sub.b = 12 //访问父类Base1的成员属性
    22. sub.c = 22 //访问父类Base2的成员属性
    23. sub.Base1.a = 11 //访问父类Base1的同名成员属性
    24. sub.Base2.a = 21 //访问父类Base2的同名成员属性
    25. }

    8、菱形继承

    两个派生类继承同一个基类,又有某类同时继承了两个派生类,这种继承被称为菱形继承,或钻石继承。如下图所示,像个菱形一样的继承:

    举个栗子,如下图所示:

    C++ 菱形继承

    (1)菱形继承

    1. #include
    2. using namespace std;
    3. //基类 - 动物
    4. class Animal
    5. {
    6. public:
    7. int age;
    8. };
    9. //派生类 - 羊 (继承动物类)
    10. class Sheep : public Animal
    11. {
    12. };
    13. //派生类 - 骆驼 (继承动物类)
    14. class Camel : public Animal
    15. {
    16. };
    17. //派生类 - 羊驼 (继承羊类 与 骆驼类)
    18. class Alpaca : public Sheep, public Camel
    19. {
    20. };
    21. int main()
    22. {
    23. //类和对象 - 菱形继承
    24. Alpaca alpaca;
    25. alpaca.Sheep::age = 18;
    26. alpaca.Camel::age = 20;
    27. //在菱形继承中,两个父类拥有相同的数据,需要加以作用域区分
    28. //在实际中,羊驼的年龄记录一个就够了,不需要两份数据,导致数据不一致,也导致资源浪费
    29. cout << "在父类Sheep中,羊驼的年龄 age = " << alpaca.Sheep::age << endl;
    30. cout << "在父类Camel中,羊驼的年龄 age = " << alpaca.Camel::age << endl;
    31. system("pause");
    32. return 0;
    33. }

    输出结果

    在父类Sheep中,羊驼的年龄 age = 18
    在父类Camel中,羊驼的年龄 age = 20

    (2)抛出问题

    在实际中,羊驼的年龄记录一个就够了,不需要两份数据,导致数据不一致,也导致资源浪费。

    查看内存对象模型命令:cl /d1 reportSingleClassLayoutAlpaca "文件名.cpp"

    (3)解决问题(virtual)

    利用虚继承解决菱形继承问题。

    虚继承:在派生类前加上关键字 virtual 

    由于派生类为虚继承,所以基类变为虚基类

    1. #include
    2. using namespace std;
    3. //基类 - 动物 - 由于派生类为虚继承,所以基类变为虚基类
    4. class Animal
    5. {
    6. public:
    7. int age;
    8. };
    9. //派生类 - 羊 (继承动物类) - 虚继承
    10. class Sheep : virtual public Animal
    11. {
    12. };
    13. //派生类 - 骆驼 (继承动物类) - 虚继承
    14. class Camel : virtual public Animal
    15. {
    16. };
    17. //派生类 - 羊驼 (继承羊类 与 骆驼类)
    18. class Alpaca : public Sheep, public Camel
    19. {
    20. };
    21. int main()
    22. {
    23. //类和对象 - 解决菱形继承问题 - 虚继承
    24. //菱形继承问题:在实际中,羊驼的年龄记录一个就够了,不需要两份数据,导致数据不一致,也导致资源浪费
    25. Alpaca alpaca;
    26. alpaca.Sheep::age = 18;
    27. alpaca.Camel::age = 20;
    28. cout << "在父类Sheep中,羊驼的年龄 age = " << alpaca.Sheep::age << endl;
    29. cout << "在父类Camel中,羊驼的年龄 age = " << alpaca.Camel::age << endl;
    30. cout << "在自己类Alpaca中,羊驼的年龄 age = " << alpaca.age << endl;
    31. system("pause");
    32. return 0;
    33. }

    输出结果

    在父类Sheep中,羊驼的年龄 age = 20
    在父类Camel中,羊驼的年龄 age = 20
    在自己类Alpaca中,羊驼的年龄 age = 20

    查看内存对象模型

    命令:  cl /d1 reportSingleClassLayoutAlpaca "文件名.cpp"

    vbptr 虚基类指针,v - virtual, b - base, ptr - pointer

    vbptr指向一个vbtable 虚基类表

    Go语言 菱形继承

    (1)菱形继承

    1. package main
    2. import "fmt"
    3. //基类 - 动物
    4. type Animal struct {
    5. age int
    6. }
    7. //派生类 - 羊 (继承动物类)
    8. type Sheep struct {
    9. Animal
    10. }
    11. //派生类 - 骆驼 (继承动物类)
    12. type Camel struct {
    13. Animal
    14. }
    15. //派生类 - 羊驼 (继承羊类 与 骆驼类)
    16. type Alpaca struct {
    17. Sheep
    18. Camel
    19. }
    20. func main() {
    21. //类和对象 - 菱形继承
    22. alpaca := Alpaca{}
    23. alpaca.Sheep.age = 18
    24. alpaca.Camel.age = 20
    25. fmt.Println("在父类Sheep中,羊驼的年龄 age = ", alpaca.Sheep.age)
    26. fmt.Println("在父类Camel中,羊驼的年龄 age = ", alpaca.Camel.age)
    27. }

     输出结果

    在父类Sheep中,羊驼的年龄 age = 18
    在父类Camel中,羊驼的年龄 age = 20

    (2)抛出问题

    同C++中抛出的问题一样:在实际中,羊驼的年龄记录一个就够了,不需要两份数据,导致数据不一致,也导致资源浪费。

    (3)解决问题(interface)

    Go语言中没有virtual来表示虚继承,可以通过 interface + struct 来实现虚基类的用法,需要实现interface中定义的方法。

    1. package main
    2. import "fmt"
    3. //接囗(虚基类) - 动物
    4. type IAnimal interface
    5. {
    6. setAge(age int)
    7. getAge() int
    8. }
    9. //接囗实现 - 动物
    10. type AnimalImpl struct {
    11. age int
    12. }
    13. func (m *AnimalImpl) setAge(age int) {
    14. m.age = age
    15. }
    16. func (m *AnimalImpl) getAge() int {
    17. return m.age
    18. }
    19. //派生类 - 羊 (继承接囗)
    20. type Sheep struct {
    21. IAnimal
    22. }
    23. //派生类 - 骆驼 (继承接囗)
    24. type Camel struct {
    25. IAnimal
    26. }
    27. //派生类 - 羊驼 (继承羊类 与 骆驼类)
    28. type Alpaca struct {
    29. Sheep
    30. Camel
    31. }
    32. func main() {
    33. //实现IAnimal接囗动物变量
    34. var animalImpl = AnimalImpl{}
    35. //羊驼变量
    36. var alpaca = Alpaca{
    37. Sheep{&animalImpl},
    38. Camel{&animalImpl},
    39. }
    40. alpaca.Sheep.setAge(18)
    41. alpaca.Camel.setAge(20)
    42. fmt.Println("在父类Sheep中,羊驼的年龄 age = ", alpaca.Sheep.getAge())
    43. fmt.Println("在父类Camel中,羊驼的年龄 age = ", alpaca.Camel.getAge())
    44. }

    输出结果 

    在父类Sheep中,羊驼的年龄 age =  20
    在父类Camel中,羊驼的年龄 age =  20

  • 相关阅读:
    乱七八糟:程序员(媛)请保护好自己
    【软件工程】金管局计算机岗位——软件测试的分类(⭐⭐⭐⭐)
    AlibabaCloud微服务:Linux 部署 Nacos 服务治理
    docker运行镜像、docker删除镜像、docker运行容器、docker退出当前容器、docker关闭容器、docker重新回到后台运行的容器命令
    pgsql查询分组中某个字段最大或者最小的一条数据
    电压提前/滞后电路 —— 电赛综测备赛
    聊聊自制的探索大全扑克牌
    密码学系列之:PKI的证书格式表示X.509
    实验七 循环神经网络(2)梯度爆炸实验
    基于动态邻域的切换粒子群优化算法
  • 原文地址:https://blog.csdn.net/ling1998/article/details/125981323