Skip to content

Commit 66eb996

Browse files
committed
udate content of c++
1 parent 5648c83 commit 66eb996

File tree

13 files changed

+587
-98
lines changed

13 files changed

+587
-98
lines changed

C++/Basic.md

Lines changed: 109 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -215,35 +215,118 @@ struct 或者 union 成员对齐规则如下:
215215
[溢出,大小端](http://www.nowcoder.com/questionTerminal/c7298be0b2ce42698b80987631cf8fca)
216216
[大小端,网络发送](http://www.nowcoder.com/questionTerminal/d7c1ff50fab44443b61903eccd791f1d)
217217

218-
# 四种类型转换
219-
220-
* reinterpret_cast:一个指针转化为其他类型的指针时,不做类型检测,操作结果是一个指针指向另一个指针的值的二进制拷贝;
218+
# 类型转换
219+
220+
类型转换就是将给定类型的表达式转换为另一种类型。C++中的转型可分为两种:隐式类型转换和显式类型转换。
221+
222+
隐式类型转换是C中的遗留物,在C++中并不推荐使用(C++有专门的转型操作符,见下文的显式转型)。将某种类型的对象拷贝到另一种不同类型的对象中时就会发生隐式转型。比如异型赋值,返回值(函数声明的返回值与代码块实际返回值不同的情况下),按值传递异型参数等情况均会发生隐式类型转换。
223+
224+
```c++
225+
short a = 128;
226+
int b;
227+
b = a;
228+
```
229+
230+
short 类型的对象被赋值给 int 型的对象,这是C++语言内建支持的标准转换。隐式类型转换是件麻烦事,它们很可能导致错误或非预期的函数被调用(参看ME 条款5)。
231+
232+
因此应该尽量避免隐式类型转换,为此 C++ 提供了显式类型转换关键字:static_cast, const_cast, dynamic_cast 以及 reinterpret_cast。
233+
234+
## static_cast
235+
236+
static_cast 很像 C 语言中的旧式类型转换。可以用于以下场景:
221237

222-
class A{};
223-
class B{};
224-
A* a = new A;
225-
B* b = reinterpret_cast(a);
238+
* 用于在存有继承关系的类之间的指针或引用的转换(即可将基类转换为子类,也可将子类转换为基类),把派生类的指针或引用转换成基类时是安全的;把基类指针或引用转换成派生类表示时,由于没有类型检查,所以是不安全的。
239+
* 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。
240+
* 把任何类型的表达式转换成void类型。
241+
* 还能将 non-const对象转换为 const对象(注意:反之则不行,那是const_cast的职责)。
242+
243+
如下示例:
244+
245+
```c++
246+
class CBase {};
247+
class CDerived: public CBase {};
248+
249+
double d = 3.14159265;
250+
int i = static_cast<int>(d);
251+
252+
CBase *base = new CBase;
253+
CDerived *derived = static_cast<CDerived *>(base);
254+
```
255+
256+
**static_cast 转换时并不进行运行时安全检查,所以是非安全的,很容易出问题。**因此 C++ 引入 dynamic_cast 来处理安全转型。
257+
258+
## dynamic_cast
259+
260+
dynamic_cast 主要用来在继承体系中的**安全向下转型**。它能安全地将指向基类的指针转型为指向子类的指针或引用,并获知转型动作成功是否。
261+
262+
dynamic_cast 只能用在指针和引用类型的转换中,它是唯一进行运行期(runtime)检查的类型转换符,它的主要目的就是保证转换后的类型是一个完整类型(Complete type)。dynamic_cast在转换指针类型时,如果结果不是一个Complete Type, 它会返回NULL; dynamic_cast在转换引用类型时,如果结果不是一个Complete Type,它会抛出bad_cast的异常。dynamic_cast 会动用运行时信息(RTTI)来进行类型安全检查,因此 dynamic_cast 存在一定的效率损失。
263+
264+
```c++
265+
class CBase { };
266+
class CDerived: public CBase { };
267+
268+
int main(){
269+
CBase b;
270+
CBase* pb;
271+
CDerived d;
272+
CDerived* pd;
273+
pb = dynamic_cast<CBase*>(&d); // ok: derived-to-base
274+
pd = dynamic_cast<CDerived*>(&b); // error: base-to-derived
275+
}
276+
```
277+
278+
上面的代码最后一行会出错(error: 'CBase' is not polymorphic),**因为dynamic_cast 只有在基类带有虚函数的情况下才允许将基类转换为子类**
279+
280+
```c++
281+
class CBase
282+
{
283+
virtual void dummy() { }
284+
};
285+
286+
class CDerived : public CBase {
287+
int a;
288+
};
289+
290+
int main()
291+
{
292+
CBase *pba = new CDerived;
293+
CBase *pbb = new CBase;
294+
CDerived *pd1, *pd2;
295+
pd1 = dynamic_cast<CDerived *>(pba);
296+
pd2 = dynamic_cast<CDerived *>(pbb);
297+
}
298+
```
299+
300+
上面代码中的 pd1 不为 null,而 pd2 为 null。
301+
302+
## const_cast
303+
304+
这个转换操作会操纵传递对象的const属性,或者设置或者移除该属性。
305+
306+
```c++
307+
class C{};
308+
const C* a = new C;
309+
C *b = const_cast(a);
310+
```
311+
312+
## reinterpret_cast
313+
314+
一个指针转化为其他类型的指针时,不做类型检测,操作结果是一个指针指向另一个指针的值的二进制拷贝;
315+
316+
```c++
317+
class A{};
318+
class B{};
319+
A* a = new A;
320+
B* b = reinterpret_cast(a);
321+
```
226322
227-
* static_cast:该运算符没有运行时类型检查来保证转换的安全性。可以用于以下场景:
228-
* 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换,进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
229-
* 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
230-
* 把任何类型的表达式转换成void类型。
231-
232-
class Base {};
233-
class Derive:public Base{};
234-
Base* a = new Base;
235-
Derive *b = static_cast(a);
236-
237-
* dynamic_cast:用于对象的指针和引用,当用于多态类型转换时,允许隐式转换及相反的过程中。与static_cast的不同之处在于,将一个基类对象指针(或引用)转换到派生类指针时,dynamic_cast会检测操作的有效性,如果返回的不是被请求的有效完整对象,则返回null,反之返回这个有效的对象,如果是引用返回无效时则会抛出bad_cast异常;
238-
* const_cast:这个转换操作会操纵传递对象的const属性,或者设置或者移除该属性。
239-
240-
class C{};
241-
const C* a = new C;
242-
C *b = const_cast(a);
323+
reinterpret_cast 用来执行低级转型,如将执行一个 int 的指针强转为 int。其转换结果与编译平台息息相关,不具有可移植性,因此在一般的代码中不常见到它。
324+
325+
reinterpret_cast 常用的一个用途是转换函数指针类型,即可以将一种类型的函数指针转换为另一种类型的函数指针,但这种转换可能会导致不正确的结果。总之,reinterpret_cast 只用于底层代码,一般我们都用不到它,如果你的代码中使用到这种转型,务必明白自己在干什么。
243326

244327
[虚函数继承类型转换](http://www.nowcoder.com/questionTerminal/5e5bb7214788436cb966e67305a8041e)
245328

246-
## If 判断语句
329+
# If 判断语句
247330

248331
零值的比较
249332

@@ -310,6 +393,7 @@ C++ 提供一种特殊的运算符,逗号运算符,它的优先级别最低
310393
[c++中的左值与右值](http://www.cnblogs.com/catch/p/3500678.html)
311394
[C++ Rvalue References Explained](http://thbecker.net/articles/rvalue_references/section_01.html)
312395
[C、C++内存对齐](http://www.jellythink.com/archives/413)
313-
[sizeof() a vector](http://stackoverflow.com/questions/2373189/sizeof-a-vector)
396+
[sizeof() a vector](http://stackoverflow.com/questions/2373189/sizeof-a-vector)
397+
[C++类型转换(Type Casting)详解](http://lang.9sssd.com/vcpp/art/962)
314398

315399

C++/C.md

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
需要注意的问题:
2727

28-
1. 语法问题:由于是纯文本替换,C预处理器不对宏体做任何语法检查,像缺个括号、少个分号神马的预处理器是不管的
28+
1. 语法问题:由于是纯文本替换,C预处理器不对宏体做任何语法检查,像缺个括号、少个分号之类的错误预处理器是不管的
2929
2. 算符优先级问题:不仅宏体是纯文本替换,**宏参数也是纯文本替换**。有以下一段简单的宏,实现乘法:
3030

3131
#define MULTIPLY(x, y) x * y
@@ -44,32 +44,23 @@
4444

4545
[宏定义中存在依赖时展开问题](http://www.nowcoder.com/questionTerminal/c33295e54974412095ebadab0f5bb820)
4646

47-
参考
48-
[C语言宏的特殊用法和几个坑](http://hbprotoss.github.io/posts/cyu-yan-hong-de-te-shu-yong-fa-he-ji-ge-keng.html)
49-
《Effective C++》 条款02:尽量以const, enum, inline 代替 #define,预处理器不够安全。
50-
5147
## C 风格字符串的函数
5248

5349
C风格字符串不是一种类型,而是为了表达和使用字符串而形成的一种约定俗成的写法。按此习惯写的字符串存放在`字符数组中并以空字符('\0')结束`,一般利用指针来操作这些字符串。
5450

5551
C 语言标准库提供了一组函数用于操作 C 风格字符串,定义在 cstring 头文件中。
5652

57-
1. strcat(p1, p2):将p2附加到p1之后,且会覆盖null字符,最后返回p1;[[对应题目](http://www.nowcoder.com/test/question/done?tid=2494453&qid=25523#summary)
58-
2. strlen(p):返回p的长度,**遇到空字符'\0'结束**,空字符不计算在内;[对应题目](http://www.nowcoder.com/questionTerminal/81cc723e49fc402ca7fa62a97a121251)
53+
1. strcat(p1, p2):将p2附加到p1之后,且会覆盖null字符,最后返回p1;[[题目](https://www.nowcoder.com/questionTerminal/3004651f5407480b8ae8e9dbdead9073)
54+
2. strlen(p):返回p的长度,**遇到空字符'\0'结束**,空字符不计算在内;sizeof返回数组所占的字节数。[[题目](http://www.nowcoder.com/questionTerminal/81cc723e49fc402ca7fa62a97a121251)
5955
3. strcmp(p1, p2):比较 p1 和 p2 的相等性,如果 p1 == p2,返回0,p1 > p2返回一个正值;p1 < p2 返回一个负值。
6056
4. strcpy(p1, p2):将p2拷贝给p1,返回p1。
6157

62-
[c_string_func.cpp](C++_Code/c_string_func.cpp)
58+
[c_string_func.cpp](../Coding/c_string_func.cpp)
6359

6460
[字符串常量赋值](http://www.nowcoder.com/questionTerminal/462f7c3746814b1cadde05a1084f8740)
6561
[strcpy 拷贝](http://www.nowcoder.com/questionTerminal/74d917fe09a94a2fb03b5371a2417372)
6662
[字符串常量和字符数组](http://www.nowcoder.com/questionTerminal/0db8ed5d69464f0bbb98d5eba3c08b9a)
6763

68-
参考:
69-
《C++ Primer》 Page109
70-
[What is the difference between char s[] and char *s in C?](http://stackoverflow.com/questions/1704407/what-is-the-difference-between-char-s-and-char-s-in-c)
71-
[Why do I get a segmentation fault when writing to a string initialized with “char *s” but not “char s[]”?](http://stackoverflow.com/questions/164194/why-do-i-get-a-segmentation-fault-when-writing-to-a-string-initialized-with-cha)
72-
7364
## printf 格式化字符串
7465

7566
printf 函数是一个标准库函数,它的函数原型在头文件“stdio.h”中。printf函数调用的一般形式为:
@@ -100,16 +91,9 @@ printf 函数是一个标准库函数,它的函数原型在头文件“stdio.h
10091

10192
[八进制输出](http://www.nowcoder.com/questionTerminal/25bce0284ec040fabdf6629dbd0c5dc9)
10293

103-
参考
104-
[cplusplus: printf](http://www.cplusplus.com/reference/cstdio/printf/?kw=printf)
105-
[C语言格式输出函数printf()详解](http://c.biancheng.net/cpp/html/33.html)
106-
[Where is %p useful with printf?](http://stackoverflow.com/questions/2369541/where-is-p-useful-with-printf)
107-
10894
## 整型溢出
10995

110-
对于整型溢出,分为无符号整型溢出和有符号整型溢出。
111-
112-
对于unsigned整型溢出,C的规范是有定义的——`溢出后的数会以2^(8*sizeof(type))作模运算`,也就是说,如果一个unsigned char(1字符,8bits)溢出了,会把溢出的值与256求模。
96+
对于整型溢出,分为**无符号整型溢出和有符号整型溢出**。对于unsigned整型溢出,C的规范是有定义的——`溢出后的数会以2^(8*sizeof(type))作模运算`,也就是说,如果一个unsigned char(1字符,8bits)溢出了,会把溢出的值与256求模。
11397

11498
当一个算术表达式中既有无符号数又有有符号数时,就会将有符号值转换为无符号值。
11599

@@ -127,10 +111,6 @@ printf 函数是一个标准库函数,它的函数原型在头文件“stdio.h
127111

128112
[For 循环次数](http://www.nowcoder.com/questionTerminal/7183f3428a444efe8a3f91247ddf6b7a)
129113

130-
参考
131-
[C语言的整型溢出问题](http://coolshell.cn/articles/11466.html)
132-
[从Swap函数谈加法溢出问题](http://blog.csdn.net/dataspark/article/details/9703967)
133-
134114
## 位段
135115

136116
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。
@@ -156,21 +136,30 @@ printf 函数是一个标准库函数,它的函数原型在头文件“stdio.h
156136

157137
[位域结构体的大小](http://www.nowcoder.com/questionTerminal/07adfd96a2364433a6538c9bb0fcda16)
158138

159-
参考
160-
[浅谈C语言中的位段](http://www.cnblogs.com/dolphin0520/archive/2011/10/14/2212590.html)
161-
162139
## 柔性数组
163140

164141

142+
165143
[结构体柔性数组作用](http://www.nowcoder.com/questionTerminal/be5269b8c2d340c3add69510d0089747)
166144

167-
参考
168-
[C语言结构体里的成员数组和指针](http://coolshell.cn/articles/11377.html)
169-
[Arrays of Length Zero](https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html)
145+
# 更多阅读
170146

147+
《C++ Primer》 Page109
148+
《Effective C++》 条款02:尽量以const, enum, inline 代替 #define,预处理器不够安全。
171149

172-
# 更多阅读
150+
[What is the difference between char s[] and char *s in C?](http://stackoverflow.com/questions/1704407/what-is-the-difference-between-char-s-and-char-s-in-c)
151+
[Why do I get a segmentation fault when writing to a string initialized with “char *s” but not “char s[]”?](http://stackoverflow.com/questions/164194/why-do-i-get-a-segmentation-fault-when-writing-to-a-string-initialized-with-cha)
152+
[Where is %p useful with printf?](http://stackoverflow.com/questions/2369541/where-is-p-useful-with-printf)
153+
[cplusplus: printf](http://www.cplusplus.com/reference/cstdio/printf/?kw=printf)
154+
[Arrays of Length Zero](https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html)
173155

156+
[C语言宏的特殊用法和几个坑](http://hbprotoss.github.io/posts/cyu-yan-hong-de-te-shu-yong-fa-he-ji-ge-keng.html)
174157
[C语言的谜题](http://coolshell.cn/articles/945.html)
175158
[语言的歧义](http://coolshell.cn/articles/830.html)
159+
[C语言格式输出函数printf()详解](http://c.biancheng.net/cpp/html/33.html)
160+
[C语言的整型溢出问题](http://coolshell.cn/articles/11466.html)
161+
[从Swap函数谈加法溢出问题](http://blog.csdn.net/dataspark/article/details/9703967)
162+
[浅谈C语言中的位段](http://www.cnblogs.com/dolphin0520/archive/2011/10/14/2212590.html)
163+
[C语言结构体里的成员数组和指针](http://coolshell.cn/articles/11377.html)
164+
176165

C++/Class.md

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,6 @@
4444

4545
[声明对象的坑](http://www.nowcoder.com/questionTerminal/95581302aa714466bc766bc51b5524fc)
4646

47-
## String 类的实现
48-
49-
类的构造函数,赋值函数,析构函数。
50-
51-
class String
52-
{
53-
public:
54-
String(const char *str=NULL);//普通构造函数
55-
String(const String &str);//拷贝构造函数
56-
String & operator =(const String &str);//赋值函数
57-
~String();//析构函数
58-
private:
59-
char* m_data;//用于保存字符串w
60-
};
61-
62-
具体实现([String.cpp](../Coding/String.cpp)
63-
6447
## 赋值还是构造
6548

6649
在面向对象程序设计中,对象间互相拷贝是经常进行的操作,那么调用的到底是拷贝构造函数还是赋值操作符呢?有一个简单的判定规则:
@@ -92,8 +75,7 @@
9275
关于异常抛出问题:
9376

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

9880
## 禁止对象产生在堆(栈)中
9981

@@ -179,7 +161,7 @@
179161
string bar; // 默认初始化为空 string 对象
180162
bar = "Hello"; // 为 bar 赋一个新值
181163

182-
就对象的数据成员来说,如果没有在构造函数的初始化列表中显式地初始化成员,则该成员在构造函数之前执行默认初始化,在构造函数中进行的是赋值操作。但是如果成员是 const 或者引用的时候,或者`成员属于某种类类型且该类没有定义默认构造函数`时,`必须`进行初始化。
164+
就对象的数据成员来说,如果没有在构造函数的初始化列表中显式地初始化成员,则该成员在构造函数之前执行默认初始化,在构造函数中进行的是赋值操作。**但是如果成员是 const 或者引用的时候,或者成员属于某种类类型且该类没有定义默认构造函数**时,`必须`进行初始化。
183165

184166
class ConstRef{
185167
public:

0 commit comments

Comments
 (0)