c++基础学习笔记——04-c++day06

在学习c++基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

04-c++day06

目录:
一、类和对象
1、关系运算符重载
2、函数调用运算符重载
3、不要重载&&、||
4、强化训练字符串封装
5、符号重载总结
6、继承的引出
7、继承方式
8、继承中的对象模型
9、继承中的构造和析构顺序
10、继承中的同名成员处理
11、继承中静态成员的处理
12、多继承的概念以及问题
13、菱形继承的问题以及解决方案
二、总结

一、类和对象

1、关系运算符重载

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 #include<string>
 4 using namespace std;
 5 
 6 // ==
 7 class Person
 8 {
 9 public:
10     Person(string name, int age)
11     {
12         this->m_Name = name;
13         this->m_Age = age;
14     }
15     
16     bool operator==(Person& p)
17     {
18         if(this->m_Name == p.m_Name && this->m_Age == p.m_Age)
19         {
20             return true;
21         }
22         return false;
23     }
24     //!=
25     bool operator!=(Person& p)
26     {
27         if(this->m_Name != p.m_Name == this->m_Age != p.m_Age)
28         {
29             return false;
30         }
31         return true;
32     }
33     
34 public:
35     
36     string m_Name;
37     int m_Age;
38 };
39 
40 void test01()
41 {
42     Person p1("小明", 10);
43     Person p2("小强", 15);
44     Person p3("小强", 15);
45     /*
46     int a = 10;
47     int b = 10;
48     if(a == b)
49     {
50         cout << "a和b相等" << endl;
51     }
52     */
53     
54     if(p1 == p2)
55     {
56         cout << "p1和p2相等" << endl;
57     }
58     else
59     {
60         cout << "p1和p2不相等" << endl;
61     }
62     if(p2 == p3)
63     {
64         cout << "p2和p3相等" << endl;
65     }
66     else
67     {
68         cout << "p2和p3不相等" << endl;
69     }    
70     
71     if(p1 != p2)
72     {
73         cout << "p1和p2不相等" << endl;
74     }
75     else
76     {
77         cout << "p1和p2相等" << endl;
78     }
79 }
80 
81 int main()
82 {
83     test01();
84     
85     system("pause");
86     return EXIT_SUCCESS;
87 }

2、函数调用运算符重载

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 #include<string>
 4 using namespace std;
 5 
 6 //()重载
 7 class MyPrint
 8 {
 9 public:
10     
11     void operator()(string text)
12     {
13         cout << text << endl;
14     }
15     
16     
17 };
18 
19 void test01()
20 {
21     MyPrint myPrint;
22     myPrint("hello world");//仿函数(或伪函数)
23     
24 }
25 
26 class MyAdd
27 {
28 public:
29     int operator()(int v1, int v2)
30     {
31         return v1 + v2;
32     }
33     
34 };
35 
36 void test02()
37 {
38     //MyAdd myAdd;
39     //cout << myAdd(1, 1) << endl;
40     
41     cout << MyAdd()(1, 1) << endl;//匿名对象
42 }
43 
44 int main()
45 {
46     test01();
47     
48     system("pause");
49     return EXIT_SUCCESS;
50 }

3、不要重载&&、||

不能重载operator&& 和 operator|| 的原因是,无法在这两种情况下实现内置操作符的完整语义。说得更具体一些,内置版本版本特殊之处在于:内置版本的&&和||首先计算左边的表达式,如果这完全能够决定结果,就无需计算右边的表达式了--而且能够保证不需要。我们都已经习惯这种方便的特性了。

我们说操作符重载其实是另一种形式的函数调用而已,对于函数调用总是在函数执行之前对所有参数进行求值。

4、强化训练字符串封装

练习:

字符串类的封装.cpp

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 #include"Mystring.h"
 5 
 6 //测试MyString
 7 void test01()
 8 {
 9     
10     MyString str = "abc";
11     
12     cout << str << endl;
13     
14     /*
15     cout << "请输入str新的内容:" << endl;
16     
17     cin >> str;
18     
19     cout << "新内容为:" << str << endl;
20     */
21     
22     MyString str2(str);
23     
24     MyString str3 = "aaaaa";
25     
26     str3 = str2;
27     str3 = "aaaa";
28     
29     cout << "str3 = " << str3 << endl;
30     
31     str3[0] = 'w';
32     
33     cout << "str3 第一个位置为= " << str3[0] << endl;
34     
35     MyString str4 = "";
36     str4 = str2 + str3;//字符串拼接
37     
38     if(str3 == str4)
39     {
40         cout << "str3与str4相等" << endl;
41     }
42     else
43     {
44         cout << "str3与str4不相等" << endl;
45     }
46     
47     
48     /*
49     int a = 10;
50     cin >> a;
51     cout << "a = " << a << endl;
52     */
53     
54 }
55 
56 int main()
57 {
58     test01();
59     
60     system("pause");
61     return EXIT_SUCCESS;
62 }

MyString.h

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #pragma once
 3 #include<iostream>
 4 using namespace std;
 5 
 6 class MyString
 7 {
 8     friend ostream& operator<<(ostream& cout, MyString& str);
 9     friend istream& operator>>(istream& cin, MyString& str);
10 public:
11     MyString(const char* str);
12     MyString(const MyString& str);
13     
14     ~MyString();
15     
16     //重载=运算符
17     MyString& operator=(const char* str);
18     MyString& operator=(const MyString& str);
19     
20     //重载[]运算符
21     char& operator[](int index);
22     
23     //重载+运算符
24     MyString operator+(const char* str);
25     MyString operator+(const MyString& str);
26     
27     //重载==运算符
28     bool operator==(const char* str);
29     bool operator==(const MyString& str);
30     
31 private:
32     char* pString;//执行堆区的指针
33     int m_Size;//字符串大小
34 }

MyString.cpp

  1 #include"MyString.h"
  2 
  3 ostream& operator<<(ostream& cout, MyString& str)
  4 {
  5     cout << str.pString;
  6     return cout;
  7 }
  8 
  9 //右移运算符重载
 10 istream& operator>>(istream& cin, MyString& str)
 11 {
 12     //先判断,原始是否有内容,如果有,清空
 13     if(str.pString != NULL)
 14     {
 15         delete[] str.pString;
 16         str.pString = NULL;
 17     }
 18     
 19     //让用户输入内容
 20     char buf[1024];
 21     cin >> buf;
 22     
 23     //把用户输入的字符串赋值给str
 24     this->pString = new char[strlen(buf) + 1];
 25     strcpy(this->pString, buf);
 26     this->m_Size = strlen(buf);
 27     
 28     return cin;
 29 }
 30 
 31 MyString::MyString(const char* str)
 32 {
 33     cout << "有参构造调用" << endl;
 34     this->pString = new char[strlen(str) + 1];
 35     strcpy(this->pString, str);
 36     this->m_Size = strlen(str);
 37 }
 38 
 39 MyString::MyString(const MyString& str)
 40 {
 41     this->pString = new char[strlen(str) + 1];
 42     strcpy(this->pString, str.pString);
 43     this->m_Size = str.m_Size;
 44 }
 45 
 46 MyString::~MyString()
 47 {
 48     cout << "析构函数调用" << endl;
 49     if(this->pString != NULL)
 50     {
 51         delete[] this->pString;
 52         this->pString = NULL;
 53     }
 54 }
 55 
 56 //重载=运算符
 57 MyString& MyString::operator=(const char* str)
 58 {
 59     if(this->pString != NULL)
 60     {
 61         delete[] this->pString;
 62         this->pString = NULL;
 63     }
 64     this->pString = new char[strlen(str) + 1];
 65     strcpy(this->pString, str);
 66     
 67     return *this;
 68 }
 69 
 70 MyString& MyString::operator=(const MyString& str)
 71 {
 72     if(this->pString != NULL)
 73     {
 74         delete[] this->pString;
 75         this->pString = NULL;
 76     }
 77     this->pString = new char[strlen(str.pString) + 1];
 78     strcpy(this->pString, str.pString);
 79     
 80     return *this;
 81 }
 82 
 83 char& operator[](int index)
 84 {
 85     return this->pString[index];
 86 }
 87 
 88 
 89 MyString operator+(const char* str)
 90 {
 91     //计算返回的字符串开辟的大小
 92     int newSize = this->m_Size + strlen(str) + 1;
 93     
 94     char* tmp = new char[newSize];
 95     
 96     memset(tmp, 0, newSize);
 97     
 98     //拼接字符串
 99     strcat(tmp, this->pString);
100     strcat(tmp, str);
101     
102     MyString newStr(tmp);
103     delete[] tmp;
104     return newStr;
105 }
106 
107 MyString operator+(const MyString& str)
108 {
109     //计算返回的字符串开辟的大小
110     int newSize = this->m_Size + strlen(str.pString) + 1;
111     
112     char* tmp = new char[newSize];
113     
114     memset(tmp, 0, newSize);
115     
116     //拼接字符串
117     strcat(tmp, this->pString);
118     strcat(tmp, str.pString);
119     
120     MyString newStr(tmp);
121     delete[] tmp;
122     return newStr;    
123 }
124 bool operator==(const char* str)
125 {
126     if(strcmp(this->pString, str) == 0 && this->mSize == strlen(str))
127     {
128         return true;
129     }
130     return false;
131 }
132 
133 bool operator==(const MyString& str)
134 {
135     if(strcmp(this->pString, str.pString) == 0 && this->mSize == strlen(str.pString))
136     {
137         return true;
138     }
139     return false;
140 }

5、符号重载总结

1) =, [], ()-> 操作符只能通过成员函数进行重载

2) <<>>只能通过全局函数配合友元函数进行重载

3)不要重载 &&|| 操作符,因为无法实现短路规则

常规建议

6、继承的引出

 c++最重要的特征是代码重用,通过继承机制可以利用已有的数据类型来定义新的数据类型,新的类不仅拥有旧类的成员,还拥有新定义的成员。
一个B类继承于A类,或称从类A派生类B。这样的话,类A成为基类(父类), 类B成为派生类(子类)。
派生类中的成员,包含两大部分:

1)一类是从基类继承过来的,一类是自己增加的成员。

2)从基类继承过过来的表现其共性,而新增的成员体现了其个性。

练习:

  1 #define _CRT_SECURE_NO_WARNINGS
  2 #include<iostream>
  3 using namespace std;
  4 
  5 /*
  6 class News
  7 {
  8 public:
  9     void header()
 10     {
 11         cout << "公共头部" << endl;
 12     }
 13     void footer()
 14     {
 15         cout << "公共底部" << endl;
 16     }
 17     void left()
 18     {
 19         cout << "左侧列表" << endl;
 20     }
 21     void content()
 22     {
 23         cout << "新闻播放" << endl;
 24     }
 25     
 26 };
 27 
 28 class YULE
 29 {
 30 public:
 31     void header()
 32     {
 33         cout << "公共头部" << endl;
 34     }
 35     void footer()
 36     {
 37         cout << "公共底部" << endl;
 38     }
 39     void left()
 40     {
 41         cout << "左侧列表" << endl;
 42     }
 43     void content()
 44     {
 45         cout << "白百何。。。" << endl;
 46     }
 47     
 48 };
 49 
 50 void test01()
 51 {
 52     //新闻页
 53     News news;
 54     news.header();
 55     news.footer();
 56     news.left();
 57     news.content();
 58     
 59     //娱乐页
 60     YULE yl;
 61     yl.header();
 62     yl.footer();
 63     yl.left();
 64     yl.content();
 65 };
 66 */
 67 
 68 //继承写法
 69 //抽象一个基类的网页,重复的代码都写到这个网页上
 70 class BasePage
 71 {
 72 public:
 73     void header()
 74     {
 75         cout << "公共头部" << endl;
 76     }
 77     void footer()
 78     {
 79         cout << "公共底部" << endl;
 80     }
 81     void left()
 82     {
 83         cout << "左侧列表" << endl;
 84     }    
 85 };
 86 
 87 class News :public BasePage//继承,News类继承于BasePage类
 88 {
 89 public:    
 90     void content()
 91     {
 92         cout << "新闻播放" << endl;
 93     }
 94 };
 95 
 96 class YULE :public BasePage//继承,YULE类继承于BasePage类
 97 {
 98 public:    
 99     void content()
100     {
101         cout << "白百何。。。" << endl;
102     }
103 };
104 
105 class Game :public BasePage//继承,Game类继承于BasePage类
106 {
107 public:    
108     void content()
109     {
110         cout << "KPL直播" << endl;
111     }
112 };
113 
114 void test02()
115 {
116     cout << "新闻网页内容:" << endl;
117     News news;
118     news.header();
119     news.footer();
120     news.left();
121     news.content();
122 
123     cout << "娱乐网页内容:" << endl;
124     YULE yl;
125     yl.header();
126     yl.footer();
127     yl.left();
128     yl.content();
129     
130     cout << "游戏网页内容:" << endl;
131     Game game;
132     game.header();
133     game.footer();
134     game.left();
135     game.content();
136 }
137 
138 //继承:减少代码重复内容
139 //BasePage 基类(父类)News派生类(子类)
140 
141 int main()
142 {
143     test01();
144     
145     system("pause");
146     return EXIT_SUCCESS;
147 }

7、继承方式

重点:掌握一张图!

练习:

  1 #define _CRT_SECURE_NO_WARNINGS
  2 #include<iostream>
  3 using namespace std;
  4 
  5 class Base1
  6 {
  7 public:
  8     int m_A;
  9 protected:
 10     int m_B;
 11 private:
 12     int m_C;
 13 };
 14 
 15 //---------公有继承------------------
 16 class Son1 :public Base1
 17 {
 18 public:
 19     void func()
 20     {
 21         //cout << m_C << endl;//基类中私有的属性,不可继承
 22         cout << m_A << endl;//基类中公有的属性,可继承,还是public
 23         cout << m_B << endl;//基类中保护的属性,可继承,还是protected,类外访问不到
 24     }
 25     
 26 };
 27 
 28 void myFunc1()
 29 {
 30     Son1 s1;
 31     s1.m_A;
 32     //s1.m_B;
 33 }
 34 
 35 //-----------保护继承-----------------
 36 class Base2
 37 {
 38 public:
 39     int m_A;
 40 protected:
 41     int m_B;
 42 private:
 43     int m_C;
 44 };
 45 class Son2 :protected Base2
 46 {
 47 public:
 48     void func()
 49     {
 50         //cout << m_C << endl;//基类中私有的属性,不可继承
 51         cout << m_A << endl;//基类中公有的属性,可继承,是protected,类外访问不到
 52         cout << m_B << endl;//基类中保护的属性,可继承,还是protected
 53     }
 54     
 55 };
 56 
 57 void myFunc2()
 58 {
 59     Son2 s2;
 60     s2.m_A;//不能访问
 61 }
 62 
 63 //-----------私有继承-----------
 64 class Base3
 65 {
 66 public:
 67     int m_A;
 68 protected:
 69     int m_B;
 70 private:
 71     int m_C;
 72 };
 73 class Son3 :protected Base3
 74 {
 75 public:
 76     void func()
 77     {
 78         //cout << m_C << endl;//基类中私有的属性,不可继承
 79         cout << m_A << endl;//基类中公有的属性,可继承,是private
 80         cout << m_B << endl;//基类中保护的属性,可继承,是private
 81     }
 82     
 83 };
 84 
 85 class GrandSon3 :protected Son3
 86 {
 87 public:
 88     void myFunc()
 89     {
 90         //cout << m_A << endl;//孙子类中访问不到m_A,因为Son3中m_A是private
 91     }
 92     
 93 };
 94 
 95 
 96 int main()
 97 {
 98     
 99     system("pause");
100     return EXIT_SUCCESS;
101 }

8、继承中的对象模型

子类会继承父类中所有的内容 ,包括了 私有属性,只是我们访问不到,编译器给隐藏了,可以通过命令查看: cl /d1 reportSingleClassLayout类名 文件名

在VS2013中 找到并双击打开“开发人员命令提示”下(默认文件夹C:Program Files (x86)Microsoft Visual Studio 12.0Common7ToolsShortcuts);先转到代码的盘符下(如:D:),然后定位到代码的文件夹下(在VS2013界面下定位找到项目的路径):cd 绝对路径;输入:dir查看一下(是否有test.cpp),然后输入:cl /d1 reportSingleClassLayoutSon test.cpp(报个单个类的布局+类名 文件.cpp)

查看类内部结构:

test.cpp

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class Base
 6 {
 7 public:
 8     int m_A;
 9 protected:
10     int m_B;
11 private:
12     int m_C;
13 };
14 
15 //子类中会继承父类的私有成员,只是被编译器给隐藏起来,访问不到私有成员
16 
17 class Son :Public Base
18 {
19 public:
20     int m_D;
21     
22 };
23 
24 void test01()
25 {
26     cout << sizeof(Son) << endl;
27 }
28 
29 int main()
30 {
31     test01();
32     
33     system("pause");
34     return EXIT_SUCCESS;
35 }

9、继承中的构造和析构顺序

子类创建对象时,先调用父类的构造,然后调用自身构造,析构顺序与构造顺序相反。

练习:

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class Base
 6 {
 7 public:
 8     Base()
 9     {
10         cout << "Base默认构造函数调用" << endl;
11     }
12     ~Base()
13     {
14         cout << "Base析构函数调用" << endl;
15     }
16     
17 };
18 
19 //子类会继承父类的成员属性,成员函数
20 //但是子类不会继承父类构造函数和析构函数
21 //只有父类自己知道如何构造和析构自己的属性,而子类不知道
22 
23 class Son :public Base
24 {
25 public:
26     Son()
27     {
28         cout << "Son默认构造函数调用" << endl;
29     }
30     ~Son()
31     {
32         cout << "Son析构函数调用" << endl;
33     }
34     
35 }
36 
37 
38 void test01()
39 {
40     //Base b1;
41     
42     Son s1;
43 }
44 
45 int main()
46 {
47     test01();
48     
49     system("pause");
50     return EXIT_SUCCESS;
51 }

 补充内容,如果父类中没有合适默认构造,那么子类可以利用初始化列表的方式显示的调用父类的其他构造。

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class Base
 6 {
 7 public:
 8     Base()
 9     {
10         cout << "Base默认构造函数调用" << endl;
11     }
12     ~Base()
13     {
14         cout << "Base析构函数调用" << endl;
15     }
16     
17 };
18 
19 //子类会继承父类的成员属性,成员函数
20 //但是子类不会继承父类构造函数和析构函数
21 //只有父类自己知道如何构造和析构自己的属性,而子类不知道
22 
23 class Son :public Base
24 {
25 public:
26     Son()
27     {
28         cout << "Son默认构造函数调用" << endl;
29     }
30     ~Son()
31     {
32         cout << "Son析构函数调用" << endl;
33     }
34     
35 }
36 
37 
38 void test01()
39 {
40     //Base b1;
41     
42     Son s1;
43 }
44 
45 class Base2
46 {
47 public:
48     Base2(int a)
49     {
50         this->m_A = a;
51         cout << "有参构造函数调用" << endl;
52     }
53     
54     int m_A;
55 };
56 
57 class Son2:public Base2
58 {
59 public:
60     Son2(int a):Base(a)//利用初始化列表方式,显示调用有参构造
61     {
62         
63     }
64     
65 };
66 
67 void test02()
68 {
69     Son2 s2(1000);
70 }
71 
72 int main()
73 {
74     test01();
75     
76     system("pause");
77     return EXIT_SUCCESS;
78 }

10、继承中的同名成员处理

查看类内部结构:

练习:test.cpp

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class Base
 6 {
 7 public:
 8     Base()
 9     {
10         m_A = 100;
11     }
12     
13     void func()
14     {
15         cout << "Base func调用" << endl;
16     }
17     void func(int a)
18     {
19         cout << "Base func(int a)调用" << endl;
20     }
21     
22     int m_A;
23 };
24 
25 class Son:public Base
26 {
27 public:
28     Son()
29     {
30         m_A = 200;
31     }
32     
33     void func()
34     {
35         cout << "Son func调用" << endl;
36     }
37 
38     
39     int m_A;
40 };
41 
42 void test01()
43 {
44     Son s1;
45     cout << s1.m_A << endl;
46     
47     //想调用父类中的m_A
48     cout << s1.Base::m_A << endl;
49     
50     s1.func();
51     
52     //想调用父类中的func
53     cout << s1.Base::func(10) << endl;
54 }
55 
56 //如果子类和父类拥有同名的函数和属性,子类会覆盖父类的成员吗?不会
57 //如果子类和父类的成员函数名称相同,子类会把父类的所有的同名版本都隐藏掉
58 //想调用父类的方法,必须加作用域
59 
60 int main()
61 {
62     test01();
63     
64     system("pause");
65     return EXIT_SUCCESS;
66 }

11、继承中静态成员的处理


 静态成员函数和非静态成员函数的共同点:
1.    他们都可以被继承到派生类中。
2.    如果重新定义一个静态成员函数,所有在基类中的其他重载函数会被隐藏。
3.    如果我们改变基类中一个函数的特征,所有使用该函数名的基类版本都会被隐藏。
静态成员函数不能是虚函数(virtual function).


类似非静态成员函数处理,如果想访问父类中的成员,加作用域即可。

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class Base
 6 {
 7 public:
 8     static void func()
 9     {
10         cout << "base func()" << endl;
11     }
12     static void func(int a)
13     {
14         cout << "base func(int)" << endl;
15     }
16     static int m_A;
17     
18 };
19 int Base::m_A = 10;
20 
21 class Son:public Base
22 {
23 public:
24     static void func()
25     {
26         cout << "son func()" << endl;
27     }
28 
29 
30     static int m_A;
31 };
32 int Son::m_A = 20;
33 
34 //静态成员属性,子类可以继承下来
35 void test01()
36 {
37     cout << Son::m_A << endl;
38     //访问父类的m_A
39     cout << Base::m_A << endl;
40     
41     Son::func();
42     //访问父类中重名的函数func()
43     Son::Base::func(10);
44 }
45 
46 int main()
47 {
48     test01();
49     
50     system("pause");
51     return EXIT_SUCCESS;
52 }

12、多继承的概念以及问题


我们可以从一个类继承,我们也可以能同时从多个类继承,这就是多继承。但是由于多继承是非常受争议的,从多个类继承可能会导致函数、变量等同名导致较多的歧义。

多继承会带来一些二义性的问题, 如果两个基类中有同名的函数或者变量,那么通过派生类对象去访问这个函数或变量时就不能明确到底调用从基类1继承的版本还是从基类2继承的版本?

解决方法就是显示指定调用那个基类的版本。


 练习:

查看类内部结构:

test.cpp

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class Base1
 6 {
 7 public:
 8     Base1()
 9     {
10         m_A = 10;
11     }
12 
13     int m_A;
14     
15 };
16 
17 class Base2
18 {
19 public:
20 public:
21     Base2()
22     {
23         m_A = 20;
24     }
25     int m_A;
26     
27 };
28 
29 //多继承
30 class Son :public Base1, public Base2
31 {
32 public:
33     int m_C;
34     int m_D;    
35 };
36 
37 //多继承中很容易引发二义性
38 void test01()
39 {
40     cout << sizeof(Son) << endl;
41     
42     Son s1;
43     //s1.m_A;//二义性
44     
45     cout << s1.Base1::m_A << endl;
46     cout << s1.Base2::m_A << endl;
47 }
48 
49 int main()
50 {
51     test01();
52     
53     system("pause");
54     return EXIT_SUCCESS;
55 }

13、菱形继承的问题以及解决方案


 两个派生类继承同一个基类而又有某个类同时继承者两个派生类,这种继承被称为菱形继承,或者钻石型继承

这种继承所带来的问题:
1.    羊继承了动物的数据和函数,鸵同样继承了动物的数据和函数,当草泥马调用函数或者数据时,就会产生二义性。
2.    草泥马继承自动物的函数和数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。

上述问题如何解决?对于调用二义性,那么可通过指定调用那个基类的方式来解决,那么重复继承怎么解决?

对于这种菱形继承所带来的两个问题,c++为我们提供了一种方式,采用虚基类。

当使用虚继承时,虚基类是被共享的,也就是在继承体系中无论被继承多少次,对象内存模型中均只会出现一个虚基类的子对象(这和多继承是完全不同的)。即使共享虚基类,但是必须要有一个类来完成基类的初始化(因为所有的对象都必须被初始化,哪怕是默认的),同时还不能够重复进行初始化,那到底谁应该负责完成初始化呢?C++标准中选择在每一次继承子类中都必须书写初始化语句(因为每一次继承子类可能都会用来定义对象),但是虚基类的初始化是由最后的子类完成,其他的初始化语句都不会调用。

注意:
    虚继承只能解决具备公共祖先的多继承所带来的二义性问题,不能解决没有公共祖先的多继承的.

工程开发中真正意义上的多继承是几乎不被使用,因为多重继承带来的代码复杂性远多于其带来的便利,多重继承对代码维护性上的影响是灾难性的,在设计方法上,任何多继承都可以用单继承代替。


 (1)练习:

查看类内部结构1:

对比分析:(vbptr——虚基类指针):指向一张 虚基类表,通过表找到偏移量,找到共有的数据

test.cpp

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class Animal
 6 {
 7 public:
 8     int m_Age;
 9 };
10 
11 //虚基类 Sheep
12 class Sheep :virtual public Animal
13 {
14     
15 };
16 //虚基类 Tuo
17 class Tuo :virtual public Animal
18 {
19     
20 };
21 
22 class SheepTuo :public Sheep, public Tuo
23 {
24     
25 };
26 
27 //菱形继承的解决方案——利用虚继承
28 //操作的是共享的一份数据
29 void test01()
30 {
31     SheepTuo st;
32     st.Sheep::m_Age = 10;
33     st.Tuo::m_Age = 20;
34     
35     cout << st.Sheep::m_Age << endl;
36     cout << st.Tuo::m_Age << endl;
37     
38     cout << st.m_Age << endl;//可以直接访问,原因已经没有二义性了,只有一份m_Age
39 }
40 
41 int main()
42 {
43     test01();
44     
45     system("pause");
46     return EXIT_SUCCESS;
47 }

(2)虚基类的内部工作原理

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<iostream>
 3 using namespace std;
 4 
 5 class Animal
 6 {
 7 public:
 8     int m_Age;
 9 };
10 
11 //虚基类 Sheep
12 class Sheep :virtual public Animal
13 {
14     
15 };
16 //虚基类 Tuo
17 class Tuo :virtual public Animal
18 {
19     
20 };
21 
22 class SheepTuo :public Sheep, public Tuo
23 {
24     
25 };
26 
27 //菱形继承的解决方案——利用虚继承
28 //操作的是共享的一份数据
29 void test01()
30 {
31     SheepTuo st;
32     st.Sheep::m_Age = 10;
33     st.Tuo::m_Age = 20;
34     
35     cout << st.Sheep::m_Age << endl;
36     cout << st.Tuo::m_Age << endl;
37     
38     cout << st.m_Age << endl;//可以直接访问,原因已经没有二义性了,只有一份m_Age
39 }
40 
41 //通过地址找到偏移量
42 //内部工作原理
43 void test02()
44 {
45     SheepTuo st;
46     st.m_Age = 100;
47     
48     //找到Sheep的偏移量操作 8
49     cout << *(int*)((int*)*(int*)&st + 1) << endl;
50     
51     //找到Tuo的偏移量 4
52     cout << *(int*)((int*)*((int*)&st + 1) + 1) << endl;
53     
54     //输出Age 100
55     cout << ((Animal*)((char*)&st + *(int*)((int*)*(int*)&st + 1))) << endl;
56 }
57 
58 int main()
59 {
60     test01();
61     
62     system("pause");
63     return EXIT_SUCCESS;
64 }

二、总结

1    关系运算符重载
1.1    自定义数据类型 不会内部做 比较 ==  !=
1.2    所以要重载  == !=
2    函数调用运算符重载  
2.1    ()  仿函数  对象() 看似像函数调用
2.2    MyAdd()   匿名对象
3    不要重载 && 和 ||
3.1    本身有短路特性我们无法去实现这种特性
3.2    所以不要重载
4    强化训练 –字符串封装
4.1    cout 输入 自定义的字符串
4.2    cin 让用户输入字符串内容
4.3    重载 = 运算符
4.4    重载 + 运算符
4.5    重载 [] 运算符
4.6    重载 == 运算符
5    继承的引出
5.1    网页 很多公共部分
5.2    导致实现时候有很多重复的代码
5.3    引出继承,基类 (父类) 公共网页  
5.4    具体子类 实现不同的内容
5.5    语法  class 子类  :继承方式 父类
6    继承方式
6.1    不管公有继承 保护 还是私有 基类中的私有属性 ,都不可以继承下去
6.2    公有继承  
6.2.1    父类中的protected 在子类中是 protected
6.2.2    父类中的public 在子类中是 public
6.3    保护继承
6.3.1    父类中的protected 在子类中是 protected
6.3.2    父类中的public 在子类中是 protected
6.4    私有继承
6.4.1    父类中的protected 在子类中是 private
6.4.2    父类中的public 在子类中是 private
7    继承中的对象模型
7.1    子类会继承父类中所有的内容 ,包括了 私有属性
7.2    只是我们访问不到,编译器给隐藏了
7.3    cl /d1 reportSingleClassLayout类名 文件名
8    继承中的构造和析构顺序
8.1    子类创建对象时,先调用父类的构造,然后调用自身构造
8.2    析构顺序与构造顺序相反
8.3    子类是不会继承父类的构造函数和析构函数
8.4    补充内容,如果父类中没有合适默认构造,那么子类可以利用初始化列表的方式显示的调用父类的其他构造
9    继承中的同名处理
9.1    成员属性 直接调用先调用子类,如果想调用父类  需要作用域
9.2    成员函数  直接调用先调用子类,父类的所有版本都会被隐藏,除非显示用作用域运算符去调用
10    继承中静态成员的处理
10.1    类似非静态成员函数处理
10.2    如果想访问父类中的成员,加作用域即可
11    多继承的概念以及问题
11.1    class A : public B1, public B2,….
11.2    引发二义性问题
11.3    想解决二义性问题,就需要通过作用域来进行区分
12    菱形继承问题以及解决
12.1    解决问题利用虚基类
12.2    sheepTuo内部结构
12.2.1    vbptr 虚基类指针
12.2.2    指向一张 虚基类表
12.2.3    通过表找到偏移量
12.2.4    找到共有的数据
12.3    内部工作原理 (可以不掌握)

在学习c++基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_BasicLearning_06.html