Skip to content

Commit 9cacd32

Browse files
committed
update c++
1 parent 7d87dad commit 9cacd32

File tree

6 files changed

+81
-22
lines changed

6 files changed

+81
-22
lines changed

C++/Basic.md

+18
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,24 @@
4747
int c;
4848
}
4949

50+
有时候区别声明和定义还真不是那么简单的,看下面的例子:
51+
52+
```c++
53+
struct Test
54+
{
55+
Test(int) {}
56+
Test() {}
57+
void fun() {}
58+
};
59+
void main( void )
60+
{
61+
Test a(1); // 定义了一个对象
62+
a.fun(); // 调用对象的函数
63+
Test b(); // 声明了一个函数
64+
b.fun(); // Error!!!
65+
}
66+
```
67+
5068
# 左值还是右值
5169
5270
左值与右值这两概念是从 c 中传承而来的,在 c 中,左值指的是既能够出现在等号左边也能出现在等号右边的变量(或表达式),右值指的则是只能出现在等号右边的变量(或表达式)。

C++/Class.md

+43-12
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,18 @@
6565
2. 若派生类中包含对象成员,还要进行对象成员初始化。如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序。
6666
3. 派生类构造函数。
6767

68-
析构函数正好和构造函数相反。 ([constructor_derived_class.cpp](../Coding/constructor_derived_class.cpp))
69-
70-
[构造函数中调用虚函数](http://www.nowcoder.com/questionTerminal/adb540e6222b401eb294b093b9fc6f0e)
71-
[构造函数调用次数](http://www.nowcoder.com/questionTerminal/bf70aadeb78949c2a61b1b561a0ee784)
72-
[析构的顺序](http://www.nowcoder.com/questionTerminal/ad46fe08266341b694d2ab8a78aa071f)
73-
[析构函数调用delete](http://www.nowcoder.com/questionTerminal/2dc097fd196343f88d7efde6e9447f91)
68+
析构函数正好和构造函数相反。具体看下面程序 ([constructor_derived_class.cpp](../Coding/constructor_derived_class.cpp))
7469

7570
关于异常抛出问题:
7671

7772
1. 不建议在构造函数中抛出异常。构造函数抛出异常时,析构函数将不会被执行,需要手动的去释放内存;
7873
2. **析构函数不应该抛出异常**。当析构函数中会有一些可能发生异常时,那么就必须要把这种可能发生的异常完全封装在析构函数内部,决不能让它抛出函数之外。因为**如果对象抛出异常了,异常处理模块为了维护系统对象数据的一致性,避免资源泄露,有必要调用析构函数释放资源**,这时如果析构过程又出现异常,那么谁来保证新对象的资源释放呢?前面的异常还没处理完又来了新的异常,这样可能会陷入无限的递归嵌套中。所以,从析构函数抛出异常,C++运行时系统会处于无法决断的境遇,因此C++语言担保,当处于这一点时,会调用 terminate()来杀死进程。
7974

75+
[构造函数中调用虚函数](http://www.nowcoder.com/questionTerminal/adb540e6222b401eb294b093b9fc6f0e)
76+
[构造函数调用次数](http://www.nowcoder.com/questionTerminal/bf70aadeb78949c2a61b1b561a0ee784)
77+
[析构的顺序](http://www.nowcoder.com/questionTerminal/ad46fe08266341b694d2ab8a78aa071f)
78+
[析构函数调用delete](http://www.nowcoder.com/questionTerminal/2dc097fd196343f88d7efde6e9447f91)
79+
8080
## 禁止对象产生在堆(栈)中
8181

8282
一般情况下,编写一个类,是可以在栈或者堆分配空间。但有些时候,你想编写一个只能在栈或者只能在堆上面分配空间的类。例如说在嵌入式系统中工作,为了保证不发生内存泄漏,最好保证没有任何一个类型的对象可以从 heap 中分配出来。
@@ -153,8 +153,6 @@
153153

154154
## 构造函数初始值列表
155155

156-
为变量分配地址和存储空间的称为`定义`,不分配地址的称为`声明`。一个变量可以在多个地方声明,但是只在一个地方定义。加入 extern 修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。
157-
158156
定义变量时习惯对其初始化,而非先声明、再赋值。
159157

160158
string foo = "Hello"; // 定义并初始化
@@ -179,7 +177,7 @@
179177
int &ref_j;
180178
};
181179

182-
构造函数初始值列表只说明用于初始化成员的值,而不限定初始化的具体执行顺序。成员的初始化顺序与它们`在类定义中的出现顺序一致`,第一个成员先被初始化,然后第二个,以此类推。如果一个成员是用另一个成员来初始化的,那么两个成员的初始化顺序就很关键了!(可能的话,**尽量避免使用某些成员初始化其他成员**)。
180+
**构造函数初始值列表只说明用于初始化非静态成员的值**,而不限定初始化的具体执行顺序。成员的初始化顺序与它们`在类定义中的出现顺序一致`,第一个成员先被初始化,然后第二个,以此类推。如果一个成员是用另一个成员来初始化的,那么两个成员的初始化顺序就很关键了!(可能的话,**尽量避免使用某些成员初始化其他成员**)。
183181

184182
[初始化顺序](http://www.nowcoder.com/questionTerminal/fb01e2436c6d453abbbf9801f794165b)
185183
[类定义static与const](http://www.nowcoder.com/questionTerminal/5ab084ff358e43f392833dbcd2963872)
@@ -189,11 +187,45 @@
189187

190188
## 数据成员
191189

190+
类的成员变量(数据成员)和普通变量一样,也有数据类型和名称,占用固定长度的内存空间,一般有以下几种成员变量:
191+
192+
* 普通变量:可以在构造函数中进行赋值,也可以在构造函数的初始化列表中进行初始化。
193+
* 静态变量(static):属于类所有,而不属于类的对象,因此不管类被实例化了多少个对象,该变量都只有一个。
194+
* 常量变量(const):需要进行类内进行初始化,可以在定义时初始化,或者在构造函数的初始化列表中进行。
195+
* 引用型变量:和const变量类似,需要在类内进行初始化。
196+
* static const integral 变量:对于既是const又是static 而且还是整形变量,可以直接在类的定义中初始化。short可以,但float的不可以哦。(short可以,但float的不可以)
197+
198+
static const integral 变量的示例如下:
199+
200+
```c++
201+
class A{
202+
public:
203+
static const int a=1;
204+
static const int b;
205+
// non-const static data member must be initialized out of line
206+
// static int c = 3;
207+
static int d;
208+
static const float e;
209+
// static const float f=1;
210+
};
211+
212+
const int A::b = 2;
213+
int A::d = 4;
214+
const float A::e = 5;
215+
```
216+
217+
特别注意的是在继承的时候,允许子类存在与父类同名的成员变量,但是并不覆盖父类的成员变量,他们同时存在。
218+
219+
[派生类重复定义基类数据成员](http://www.nowcoder.com/questionTerminal/ade233b99dfc4f03aba0335f9f2a3f35)
220+
192221
## 类的静态成员
193222

194223
在类成员的声明之前加上关键字 static 使得成员与类本身直接相关,而不是与类的各个对象保持关联。和其他成员一样,静态成员可以是 public 或 private 的,静态数据成员的类型可以是常量、引用、指针、类类型等。类的静态成员存在于任何对象之外,对象中不包含任何与静态数据成员有关的数据。
195224

196-
类的静态数据成员不属于类的任何一个对象,所以它们不是在创建类的对象时被定义的,也就是说不是由类的构造函数初始化的(`不能用构造函数初始化静态数据成员`)。通常情况下,类的静态成员不应该在类的内部初始化,必须在类的`外部定义和初始化`每个静态成员。一种例外情况是,为静态数据成员提供 const 整数类型的类内初始值(**即使是 const static 也不能用构造函数初始化列表来进行初始化**)。
225+
类的静态数据成员不属于类的任何一个对象,所以它们不是在创建类的对象时被定义的,也就是说不是由类的构造函数初始化的(**不能用构造函数初始化静态数据成员**)。通常情况下,类的静态成员不应该在类的内部初始化,必须在类的`外部定义和初始化`每个静态成员。一种例外情况是,为静态数据成员提供 const 整数类型的类内初始值。**不过即使是 static const 也不能用构造函数初始化列表来进行初始化**,这是因为:
226+
227+
1. static属于类,它在未实例化的时候就已经存在了,而构造函数的初始化列表,只有在实例化的时候才执行。
228+
2. static成员不属于对象。我们在调用构造函数自然是创建对象,一个跟对象没直接关系的成员要它做什么呢。
197229

198230
类似的,静态成员函数也不与任何对象绑定在一起,不包含 this 指针。`静态成员函数不能声明为 const 的(本来就不会去改变对象的值,所以没有必要定义为const),而且不能在 static 函数体内使用 this 指针`。既可以在类的内部定义静态成员函数,也可以在外部定义静态成员函数。在类的外部定义静态成员函数时,不能重复关键字 static。
199231

@@ -212,7 +244,7 @@
212244

213245
`函数隐藏`是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
214246

215-
1. 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)。
247+
1. 如果派生类的函数与基类的函数同名,但是**参数不同**。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)。
216248
2. 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual 关键字(否则是覆盖)。此时,基类的函数被隐藏(注意别与覆盖混淆)
217249

218250
当然,派生类还可以`覆盖`基类函数,以实现多态。这三种情况的执行可以总结为以下:
@@ -252,7 +284,6 @@
252284

253285
[派生类存储,隐式转换](http://www.nowcoder.com/questionTerminal/c85f9e15e6a4410a930581ae12b9a341)
254286
[子类继承父类所有对象](http://www.nowcoder.com/questionTerminal/ff91c410e28745e8ae01537d8a888283)
255-
[派生类重复定义基类数据成员](http://www.nowcoder.com/questionTerminal/ade233b99dfc4f03aba0335f9f2a3f35)
256287

257288
## 虚拟继承
258289

Coding/C++_Inheritance_2.cpp

+7-6
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ class B: public A {};
2424
int main()
2525
{
2626
A a;
27-
std::cout << *(int *)&a << std::endl;
28-
std::cout << *((int *)&a + 1) << std::endl;
29-
std::cout << *((int *)&a + 2) << std::endl;
27+
std::cout << *(int *)&a << std::endl; // 1
28+
std::cout << *((int *)&a + 1) << std::endl; // 2
29+
std::cout << *((int *)&a + 2) << std::endl; // 3
3030
B b;
31-
std::cout << *(int *)&b << std::endl;
32-
std::cout << *((int *)&b + 1) << std::endl;
33-
std::cout << *((int *)&b + 2) << std::endl;
31+
std::cout << *(int *)&b << std::endl; // 1
32+
std::cout << *((int *)&b + 1) << std::endl; // 2
33+
std::cout << *((int *)&b + 2) << std::endl; // 3
34+
return 0;
3435
}

Coding/constructor_derived_class.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,12 @@ class DerivedContainer : public BaseContainer {
5050

5151
int main() {
5252
DerivedContainer dc;
53+
/*
54+
Contained1 constructor.
55+
Contained2 constructor.
56+
BaseContainer constructor.
57+
Contained3 constructor.
58+
DerivedContainer constructor.
59+
*/
60+
return 0;
5361
}

Network/Surge.md

-3
This file was deleted.

Questions.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,14 @@
6161
- [ ] [inline 内联函数](C++/Keywords.md#inline)
6262
- [ ] [typedef 类型别名](C++/Keywords.md#typedef)
6363
- [ ] [explicit 避免隐式类型转换](C++/Keywords.md#explicit)
64+
- [ ] [赋值还是拷贝构造](C++/Class.md#赋值还是构造)
65+
- [ ] [禁止对象产生在堆栈上](C++/Class.md#禁止对象产生在堆栈中)
66+
- [ ] [类内初始化](C++/Class.md#构造函数初始值列表)
67+
- [ ] [虚函数实现机制](C++/Class.md#多态)
68+
- [ ] [函数重载、覆盖、隐藏区别](C++/Class.md#成员函数)
6469
- [ ] 数组作为形参
6570
- [ ] 指针与引用区别
6671
- [ ] 函数指针
67-
- [ ] 函数重载、覆盖、隐藏区别
6872
- [ ] [内存对齐原则](C++/Basic.md#内存对齐)
6973
- [ ] [sizeof 运算符](C++/Basic.md#sizeof-运算符)
7074
- [ ] [union 实现 CPU 字节序判定](C++/Basic.md#联合体cpu字节序)

0 commit comments

Comments
 (0)