C++ Language

First Post:

Last Update:

Word Count:
16.6k

Read Time:
86 min

C++ language

Learn:

https://www.programiz.com/cpp-programming/variables-literals

Keywords of C++

alignas(c++11)	 //声明结构体或类的对齐数
alignof(c++11)	 //查看结构体或类的对齐数
and				 //等价于 &&
and_eq			 //等价于 &&=
asm				 //汇编语句
auto			 //自动类型
bitand			 //等价于  &
bitor			 //等价于 |
bool			 //bool类型
break			 //跳出当前循环,或switch
case			 //结合switch使用
catch			 //捕获异常
char			 //1字节变量
short			 //2字节变量
signed			 //声明为有符号类型
unsigned		 //声明为无符号类型
char16_t(c++11)  //常使用于Unicode字符
char32_t(c++11)	 //常使用于Unicode字符
int				 //默认4字节
long			 //不比int类型小的类型
class			 //类,或者声明模板类型
compl			 //等价于 ~
concept(概念Ts)	 //专家级别使用
const			 //声明常变量
constexpr(c++11) //用作函数时,是在编译是进行函数调用的,类似与宏
const_cast		 //常变量强制转化为其他变量类型
continue		 //结合循环来使用	
decltype(c++11)  //与auto差不多,可以用于某个不确定多个参数类型的模板函数
default			 //结合switch来使用
delete			 //释放new出来的东西
new				 //开辟内存
do				 //配合 while构成循环
explicit		 //不能隐式构造
export			 //专家级别使用...
extern			 //改变某个变量的作用域,常用在不同文件中使用同一个变量
float			 //4字节的浮点类型
double			 //8字节的浮点类型
for				 //for循环
goto			 //跳转
if				 //判断
friend			 //给类中的一些函数使用,可以访问类中的私有成员变量
inline			 //内联声明, 编译器对该函数进行优化,类中函数和模板函数默认代这个参数
mutable			 //在lambda表达式中使用表示为传递进来的变量可以被赋值
namespace		 //声明命名空间 
noexcept(c++11)  //声明为不抛出异常,利于编译器的优化 
not				 //等价与 !
not_eq			 //等价于 !=
nullptr(c++11)	 //c++11以后声明空指针类型,主要解决模板函数中的参数问题
operator		 //操作运算重载
or				 //等价与 |
or_eq			 //等价于 |=
private			 //声明成员为私有的
public			 //声明成员为公共的
protected		 //声明成员为保护的
register		 //声明在寄存器里的变量
reinterpret_cast //常用在 一个类型的指针转化为另一个类型的指针
requires(概念TS) //一个概念,目前还没有编译器实现
return	 //函数的返回
static	 //声明变量为静态的
sizeof	 //获取某个变量的大小
static_assert(c++11) //静态断言, 常用
static_cast //静态转化
struct	 //结构体类型
switch	 //switch分支判断结构
template //声明模板
this	 //this指针, 常用在class和struct
thread_local(c++11)  //结合线程来使用
throw	 //抛出异常
true	 //bool类型的真
false	 //bool类型的假
try		 //异常尝试
typedef	 //改变变量名称
typeid	 //获取类型的id号
typename //声明模板类型
union	//联合体
using  //可以替换typedef的功能
virtual //虚函数
void   //空类型
volatile //直接声明为定死的常量
wchar_t //表示宽字符的
while   //循环
xor		//异或 ^
xor_eq  // ^=

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
#include <iostream>
#include <typeinfo>
#include <cassert>
#include <map>
//#include <algorithm>
//#include <vector>
//#include <list>
//#include <stack>
//#include <future>
//#include <functional>
using namespace std;
//---------------------------------------------------------------
//alignas
struct alignas(8) S {};
struct alignas(1) U {S s; }; //bad
struct Foo {
int i;
float f;
char c;
};
struct Empty {};
struct alignas(64) Empty64 {};
struct alignas(1) Double { double d; };
struct Obj {char a; int b;};
//sizeof(Obj) == 8
//alignof(Obj) == 4
void alignInfo() {
cout << "Alignment of" << endl
<<"_char : " << alignof(char) << endl
<<"_pointer : " << alignof(int*) << endl
<<"_class Foo : " << alignof(Foo) << endl;
}
//---------------------------------------------------------------
//and
void showAnd() {
int a = 3;
int b = 4;
if(a == 3 && b == 4) {
cout << "&& a = 3, b = 4";
}
if(a == 3 and b == 4) {
cout << "and a = 3, b = 4";
}
}
//---------------------------------------------------------------
//and_eq <=> &=
void testAnd_eq() {
bool a = false;
bool b = true;
b = b and_eq a;
cout << b << endl;
bool c = true;
c and_eq a;
cout << "c: " << c << endl;
}
//---------------------------------------------------------------
//not <=> !
void testNot() {

bool a = true;
bool b = not(a);
cout << b;
}
//---------------------------------------------------------------
//not_eq <=> !=
void testNot_eq() {
bool a = true;
a not_eq(a);
cout << a;
}
//---------------------------------------------------------------
//or <=> |
void testOr() {
int a = 3;
cout << a or 2;
}
//---------------------------------------------------------------
//or <=> |=
void testOr_eq() {
int a = 3;
a or_eq 4;
cout << a;
}
//---------------------------------------------------------------
void showAsm() {
asm (
"movq $60, %rax\n\t" //the exit syscall number on linux
"movq $2, %rdi\n\t" //this program returns 2
"syscall"
);
}
//---------------------------------------------------------------
int get_fun(int x) {
return x + 1;
}
double add(double a, double b) {
return a + b;
}
void showAuto() {
auto a = 1 + 2;
cout << "type of a: " << typeid(a).name() << endl;
auto b = add(1, 1.2);
cout << "type of b: " << typeid(b).name() << endl;
auto c = {1, 2}; //初始化列表
cout << "type of c: " << typeid(c).name() << endl;

auto my_lambda = [](int x) { return x + 3; };
cout << "type of my_lambda: " << typeid(my_lambda).name() << endl;

auto my_fun = get_fun;
cout << "my_fun: " << my_fun(3) << endl;

cout << "type of my_fun: " << typeid(my_fun).name() << endl;

}
//---------------------------------------------------------------
void showBitAndOr() {
auto a = 3l; //long int
auto b = 4;
auto c = a bitand b;
auto d = a bitor b;
cout << "c: " << c << "d: " << d;
}
//---------------------------------------------------------------
void testBool() {
bool a = true;
bool b = false;
*(reinterpret_cast<char *>(&a)) = -1;
cout << a << " " << b << endl;
if(a == true) {
cout << "i'm true";
}else if(a == false){
cout << "i'm false";
}else {
cout << "What?";
}
}
//---------------------------------------------------------------
void testBreak() {
int a = 10;
for(;;) {
for(;;) {
++a;
if(a > 1000) break;
}
if(a > 100000000) break;
}
}
//---------------------------------------------------------------
void testCompl() {
int a = -3;
int b = compl(a); // compl(a) <=> ~a
cout << b;
}
//---------------------------------------------------------------
int fact(int n) {
return n < 1 ? 1 : (n * fact(n - 1));
}
constexpr int factorial(int n) {
return n < 1 ? 1 : (n * factorial(n - 1));
}
template<int N>
struct NN{
void print() { cout << N << endl;}
};
void testConstExpr() {
auto a = fact(4); //运行时计算
auto b = factorial(4); //编译时计算, 类似于宏定义计算
cout << "a: " << a << " b: " << b << endl;
char group[factorial(5)];
NN<factorial(8)> nn;
nn.print();
}
//---------------------------------------------------------------
/*
void testThread(const int &a) {
cout << "thread"
}*/
void testConst_cast() {
const int i = 3;
//const int*
int *p = const_cast<int*>(&i);
// std::thread aa([&i](){ testThread(i); });
*p = 5;
cout << p << endl << &i << endl;
cout << i << " " << *p << endl;
}
//---------------------------------------------------------------
//decltype 与 auto 十分相近
struct A {A(int n) : x(n) {} double x;};
void testDecltype() {
const A* a = new A(0);
auto aa = a->x;
decltype(a->x) y; // 相当于auto aa = a->x
decltype((a->x)) z = y; //相当于 auto & z = y, 引用
y = 3;
// z = 4;
cout << y << endl << z;
}
// 写一个相加函数, 实现不确定类型相加的函数
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) { //decltype(a + b) 推导出返回值类型
return a + b;
}
/*
//陷阱: auto
template<typename T, typename U>
auto add(Ta, Ub) {}
return a + b;
// return a + b 与 return (a + b) 可能表示的意义不同, 后者可能代表引用
}*/
//---------------------------------------------------------------
struct Base { virtual ~Base() {} };
struct Drive : Base {
virtual void name() {}
};
void testDynamic_cast() {
Base *b1 = new Base;
//这里会调用不成功
if(Drive *d = dynamic_cast<Drive *>(b1)) {
std::cout << "downcast from b1 to d successful" << std::endl;
d->name(); //safe to call
}

Base *b2 = new Drive;
//这里会调用成功
if(Drive *d = dynamic_cast<Drive *>(b2)) {
std::cout << "downcast from b2 to d successful" << std::endl;
d->name(); //safe to call
}
//对于dynamic_cast转换引用,若转化失败,则会抛出异常
//dynamic_cast 在运行期进行检查
/*
Base bb;
Drive& cc = dynamic_cast<Drive&>(bb);
*/
}
//---------------------------------------------------------------
enum Color { red, green, blue };
//老的版本中,若出现 enmu Color2 {red, green, blue}; 就会出先重复的元素,就会报错
void testOldEnum() {
Color r = red;
switch(r) {
case red:
std::cout << "red" << std::endl;
break;
case green:
std::cout << "green" << std::endl;
break;
case blue:
std::cout << "blue" << std::endl;
break;
default:
std::cout << "what ?" << std::endl;
}
}
enum class NewColor {red, green, blue = green + 12};
enum class MyColor : short {black}; //通过short指定元素的类型为short

//若以bool作为函数参数, 建议使用enum来代替bool参数.
enum class IsGood {Yes, No};
enum class IsOk {Yes, No};
void enumBoolTest(IsGood isGood, IsOk isOk) {
}

void testNewEnum() {
NewColor r = NewColor::blue;
switch(r) {
case NewColor::blue: {
cout << "new blue" << endl;
} break;
case NewColor::red: {
cout << "new red" << endl;
} break;
case NewColor::green: {
cout << "new green" << endl;
} break;
default:
cout << "new what?" << endl;
}

int i = static_cast<int>(blue) + static_cast<int>(NewColor::blue);
cout << "blud + NewColor::blue = " << i << endl;
enumBoolTest(IsGood::Yes, IsOk::No);
}
//---------------------------------------------------------------
//test explicit
struct AA {
AA(int) {}
AA(int, int) {}
operator int() const { return 0; } //AA可以默认的转化为int类型
};
struct BB {
explicit BB(int) {}
BB(int, int) {}
explicit operator int() const { return 0; }
};
void testExplicit() {
AA a1 = 1; //等价于 AA a1 = AA(1);
//而BB就不能 BB b = 1;
AA a2 = {3, 5}; //等价于 AA a1 = AA(3, 5);
BB b2 = {3, 5}; //BB中B(int, int)没有加explicit
int i = a1; //能通过
//而BB就不能 int i = b2;
//将BB强转.
int na2 = static_cast<int>(a1);
int nb2 = static_cast<int>(b2); //强转explicit 可以
AA a4 = (AA)3;
BB b3 = (BB)4; //通过C语言方式强制转换, 可以
}
//---------------------------------------------------------------
//friend
class MyClass {};
class Friend {
private:
int data; //private member
friend std::ostream &operator << (std::ostream &out, const Friend &o);
//Mycla 类是Fried类的好友, 只是单向
template <typename T> friend class MyCla; //every MyClass<T> is a friend of Friend
template <typename T> friend void f(T) {} //every f<T> if a friend of Firend
public:
Friend(int x) : data(x) {}
operator int() const { return this->data; }
};
//这样可以拿这个函数当公共函数访问私有成员
std::ostream &operator << (std::ostream &out, const Friend &f) {
return out << f.data;
}
//若发生继承关系, MyCla不是, Friend2 的好友.
class Friend2 : public Friend { };

void testFriend() {
Friend a = 44;
cout << a;
}
//---------------------------------------------------------------
//goto
void testGoto() {
auto i = 0;
NO:
if(i < 5) {
cout << i;
i ++;
goto NO;
}
}
//---------------------------------------------------------------
//inline
//尽量的减少栈空间开辟和回收操作
//类和模板函数,默认会加上inline进行优化.
//但不一定快
//inline 也可以与 namespace一起用, inline namespace
inline int plus(int a, int b) {
return a + b;
}
class Inline {
//在类中的函数里,编译器会默认加上inline进行优化
public:
void Member() const { std::cout << "hello\n"; }
int vlaue() const { return this->m_value; }
private:
int m_value;
};
//---------------------------------------------------------------
//namespace
namespace XGroup {
class A_ {
int value;
};
}
namespace YGroup {
class A_ {
int value;
};
class B_ {
int value;
};
}
void testNmaespace() {
XGroup::A_ a;
YGroup::A_ b;
}
void testNamespace2() {
using namespace XGroup;
A_ a;
using namespace YGroup;
B_ b; //可以不用写 YGroup::B_
}
void testNamespace3() {
namespace CGroup = XGroup;
CGroup::A_ a;
using YGroup::B_;
B_ b;
}
//没有名字的namespace
namespace {
std::string astring("long"); //相当于 static std::string astirng
}
void testNamespace4() {
cout << astring; //打印astring
}
//---------------------------------------------------------------
//noexcept
void noexCept() noexcept { //声明确定不会抛出异常,可以减轻编译器压力,利于优化代码.
std::cout << "hello" << std::endl;
//std::terminate(); abort(); exit(0);
}
void noexCept2() noexcept(false) { //等价于没写
std::cout << "hello" << std::endl;
}
//---------------------------------------------------------------
void nullPointer(int *a) {
}
template<typename T, typename U>
void func(T t, U u) {
t(u);
}
void testNullPointer() {
//三者直接调用,结果一样
nullPointer(0);
nullPointer(NULL); //(void*)0
nullPointer(nullptr); //std::nullptr_t 在c++11中有专有的类型
//利用模板函数之后, NULL 模板函数推导为int
func(nullPointer, nullptr);
//不能调用 
// func(nullPointer, NULL);
// func(nullPointer, 0);
//若想调用,需要强制转换 
func(nullPointer, (int*)0);
func(nullPointer, (int*)NULL);
}
//---------------------------------------------------------------
//operator
struct Operator {
public:
Operator(int x) : data(x) { }
// operator int() const { return this->data; }
int operator << (Operator const& o) const {
return data + o.data;
}
int operator & () const { return data; }
private:
int data;
};
void testOperator() {
Operator a = 10;
Operator b = 5;
Operator c = a << b; //实际实现相加
cout << &c << endl; //采用去地址符号实现返回里面的值
}
//实例应用:
/*
struct AClass {
~AClass() { delete m_value}
AClass() : m_value(new int()) {}
private:
int *m_value;
}
void test() {
Aclass A;
Aclass B;
A = B; //这种情况,里面的值m_value,会发生直接赋值,A和B直接指向同一块内存,
//会发生两次析构,内存会错误.

} */
//使用操作符重载 = 来避免:
struct AClass {
~AClass() { delete m_value;}
AClass(const AClass& rhs) : m_value(new int(*(rhs.m_value))) {}
AClass& operator = (const AClass& rhs) {
*m_value = *(rhs.m_value);
return *this;
}
private:
int *m_value;
};
//---------------------------------------------------------------
//reinterpret_cast
void testReinterpret_cast() {
//reinterpret_cast
//static_cast
//const_cast
//dynamic_cast
//c like cast 可以实现以上的大部分, 而dynamic_cast c语言这种方式不能实现的

int a = 1;
// 也相当于 char* p = (char*)&a;
auto p = reinterpret_cast<char *>(&a); //转化为char*
//不能char* b = static_cast<char*> &b;
if(p[0] = 1) {
std::cout << "the system is little endian\n";
}else {
std::cout << "the system is big endian\n";
}
}
//---------------------------------------------------------------
//sizeof, before c++11
struct SizeofEmpty {};
struct SizeofBase { int a;};
struct SizeofDerived : Base{ int b;};
struct SizeofBit {unsigned bit : 1;};
void testSizeof() {
SizeofEmpty e;
SizeofDerived d;
SizeofBase b;
SizeofBit bit;
cout << "sizeof empty: " << sizeof e << endl;
cout << "sizeof derived: " << sizeof d << endl;
cout << "sizeof base: " << sizeof(b) << endl;
cout << "sizeof bit: " << sizeof(SizeofBit) << endl;
// cout << "szieof void" << sizeof(void) << endl;
// cout << "szieof int[]" << sizeof(int[]) << endl;
}
//---------------------------------------------------------------
//static
//最好要这么用
static int globalA = 0;
void printInfo() {
static int localStatic = 0; //多线程不要这么用
++ localStatic;
cout << "globalA: " << globalA << " " << "localStatic: " << localStatic << endl;
}
struct Static {
static int s; //注意: sizeof(Static) == 1, s的空间已经分配
};
int Static::s = 10;

struct HeHe {};
struct Static2 {
static int a[];
static HeHe foo;
static Static2 ss;
};
//对成员进行实例化
int Static2::a[10];
HeHe Static2::foo;
Static2 Static2::ss;

//---------------------------------------------------------------
//static_assert c++11的一个宏, 在编译期进行检测
//assert 是在运行的时候进行检测
void testStaticAssert() {
static_assert(sizeof(int) == 4, "only work for int of 32bit");
static_assert(sizeof(long) == sizeof(long long),
"only work for int of 32bit");
int a = 5; //判断变量 a == b 采用assert
int b = 5;
assert(a != b && "error");
}
template<typename T>
void static_assertFun() {
static_assert(alignof(T) ==4, "only for alignof 4");
}
//---------------------------------------------------------------
//typedef , 换名称
typedef unsigned long ulong;
class Comp {
typedef std::map<int, ulong> Group;
Group a; //相当于 std::map<int, ulong> a;
void aa() {
//std::map<int, ulong>::iterator iter = a.find(10)
Group::iterator iter = a.find(10);
//也可以使用: auto iter = a.fine(10);
}
};
//---------------------------------------------------------------
//using
//可以使用与namespace,也可以替换typedef
using newGroup = std::map<int, ulong>;

//using namespace std;

//---------------------------------------------------------------

int main(void) {
// showAuto();
// showBitAndOr();
// showAsm();
// testAnd_eq();
// testNot();
// testNot_eq();
// testBool();
// testBreak();
// testCompl();
// testConstExpr();
// testConst_cast();
// testDecltype();
// testDynamic_cast();
// testOldEnum();
// testNewEnum();
// testExplicit();
// testFriend();
// testGoto();
// testNamespace4();
// testOperator();
// testReinterpret_cast();
// testSizeof();
testStaticAssert();

return 0;
}

Class

C++析构函数中抛出异常问题

注意: 不要在析构函数中抛出异常

析构函数调用规则

  1. 先调用成员析构
  2. 再调用派生类析构

问题代码

分析代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
class A {
public:
A() {}
~A() { std::cout << "A析构了.." << std::endl; }
};
//析构函数绝对不要抛出异常
class B : public A{
public:
B() {}
~B() { std::cout << "B析构了.. " << std::endl;
throw std::string("B error"); } // 编译器默认析构函数: inline ~B() noexcept {}
//编译器默认析构函数实现:
/*
inline ~B() noexcept {
try {

} catch(...) {
std::terminate();
//析构函数绝对不要抛出异常
}
}
*/
private:
std::string m_value;
/*
在掉~B() 析构时候, 先调用 m_value的析构,再调用派生类 ~A();
*/
};
void testClass_1() { //测试析构
B *b = new B;
delete b;
}
class EvilB : public B {
public:
~EvilB() noexcept(false) { throw std::string("error"); }
};
void testEvilB() {
try {
EvilB *b = new EvilB();
delete b;

} catch(std::string e) {
//没有捕获到异常,编译器已经调用std::terminate(),且程序崩溃
//因为编译器为析构函数默认加入关键字 noexcept,可以加入关键字 noexcept(false)
//问题1 : 派生类虽然能够析构,若派生类析构函数也抛出异常,则程序就挂.
//出现抛出两个异常, 则无法捕获.
//问题2 : 若声明两次EvilB对象,析构两次时,则程序就会崩溃.
std::cout << "catch your evil: " << e << std::endl;
}
}
int main(void) {
testEvilB();
return 0;
}

正确做法

建议: 在类的构造函数中抛出异常

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <fstream>
#include <vector>
#include <cassert>

class A {
public:
A() {}
};
class B : public A {
public:
B() {}
~B() { /*std::cout << "byebye B\n";*/ }
private:
std::string m_value;
};
class GoodB : public B {
public:
GoodB() : m_v(new char(10000000)) {}
GoodB(const char* f) : m_file(f, std::ios_base::in) {
//在构造器里进行抛出异常,而不是一直判断是否有错误来避免错误.
if(!m_file.is_open()) throw std::string("Couldn't open file..\n");

std::string line;
while(std::getline(m_file, line)) {
m_info.push_back(line);
}
}
void doIt( GoodB const&b) {
if(b.isOpen()) {
//do
}
}
//通过析构来提示.
~GoodB() { assert((!isOpen()) && "open file: "); }
private:
char *m_v;
std::fstream m_file; //外部文件
std::vector<std::string> m_info;
//若作为状态判断,一般声明为私有函数, 通过assert来提示.
bool isOpen() const { return !m_info.empty(); }
};
//循环构造, 内存崩溃
//想实现一个 std::out_of_memory 异常
void test() {
int i = 0;
for(;;) {
try {
++i;
if(i % 10000 == 0) std::cout << "count i: " << i << std::endl;
GoodB b;
} catch(...) {
return ;
}
}
}
void test2(char *filename) {
try {
//在构造时候进行抛出异常,重复抛出, 不会出现问题, 而在析构函数中抛出,程序则会崩溃
GoodB b(filename);
GoodB d(filename);
b.doIt(b);
//std::swap(b, d) //也不要抛出异常
} catch(std::string msg) {
std::cout << msg;
}
}
int main(int argc, char** argv) {
if(argc <= 1) return 1;
// test();
test2(argv[1]);
std::cout << "exit!!!\n";
return 0;
}

管理内存的几种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <memory>

static void versionOne();
static void versionTwo();

using namespace std;

int main(void) {

versionOne();
versionTwo();
versionThree();
return EXIT_SUCCESS;
}
void versionOne() {
//c语言方式开辟内存
int *ageC = (int*) malloc(sizeof(int));
if(argC) {
free(ageC);
}
char *c = (char*) malloc(100);
free(c);

//c++ 开辟内存方式
int *age = new int(25);
int *height = new int(160);
//缺陷,容易忘掉free 或 delete
}

void versionTow() {
//智能指针
std::shared_ptr<int> age(new int(28));
std::shared_ptr<int> height(new int(160));

std::cout << "VersionTwo: you age is " << *age << ", and your height is "
<< *height << std::endl;
//不需要做任何事情, 内存会自动的释放掉
//基本上,不会造成内存泄露问题
}

逐渐退化的虚函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <map>
#include <iostream>
/*
类虚函数遇到构造和析构就退化了
*/

class Event; //类的前置声明
class Event {};
class Base {
public:
virtual ~Base() {} //why? virtual
Base(int _id) : m_id(_id) {}
virtual void act(Event const&) = 0;
virtual void print() const = 0;
int id() const { return m_id; }
private:
int m_id;
};
class Drived : public Base { //why public?
public:
void act (Event const&);
void print() const;
Drived(int id);
~Drived();
private:

};
class Grouped : public Base {
public:
void act(Event const&);
void print() const;
void addBase(Base* b);
void removeBase(int id);
Grouped(int id);
~Grouped();
private:
std::map<int, Base *> m_info;
};
void test() {
//int a = 1;
/*
Drived *info = new Drived(1);
Grouped *group = new Grouped();

group->print();
group->act(ev);
group->addBase(info);
group->removeBase(info->id());
group->id();

Base *baseGroup = group;
baseGroup->act(ev);
baseGroup->print();
baseGroup->id();
//removeBase()
//addBase()
//而基类的指针baseGroup中强制把派生类转换,而基类在虚函数中干的事,与派生类的一样
//相当于派生类来实现函数,而不是基类.这就实现了指针对接口进行编程.
delete group; //delete baseGroup;没有内存泄露, 与delete group等价
delete info;
*/
}

int main(void) {
test();
return 0 ;
}

虚函数几种实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

struct Base {
virtual void f() { std::cout << "base" << std::endl; }
virtual void init() {}
Base() {
init();
}
};

struct Derived : Base { //结构体默认为public 继承, 类默认为 private
void f() override { //加入override关键字,表示重写基类的f()虚函数
std::cout << "derived overide" << std::endl;
}
void init() override { std::cout << "virtual init" << std::endl; }
} ;

struct Derived2 : Derived {
public:
void f() {}
void init() {}
};

Base* factoryBase(int type) {
Base* ret = nullptr;
if(type == 0) {
ret = new Base();
}else if(type == 1) {
ret = new Derived();
}else if(type == 2) {
ret = new Derived2();
}

if(ret) ret->init();
return ret;
}

void testVirtual() {
//Base b;
//Derived d;
////虚函数调用通过引用
////virtual funciton call through reference
//Base& br = b; //the type of br is Base&
//Base& dr = d; //the type of dr is Base& as well
//br.f();
//dr.f(); //在Derived类中已经对fun() 重写.
//
////虚函数调用通过指针
////virtual function call through pointer
//Base* bp = &b;
//Base* dp = &d;
//bp->f();
//dp->f();

////直接调用.
////non-virtual function call
//b.Base::f();
//d.Derived::f(); //派生类
//d.Base::f(); //基类
////若在构造函数中或者析构函数中调用虚函数, 则会调用自身类中的虚函数,而不是派生类的重写的虚函数
////虚函数特性还没体现
Derived d;
}
int main(void) {
testVirtual();
return 0;
}

对象内存布局

普通对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
class A {
public:
A() {
std::cout << "A\n";
}
~A() {
std::cout << "~A\n";
}
private:
int a = 1;
int b = 2;
};

int main() {
A a;
return 0;
}

生成可执行文件ida反编译如下:

main

1
2
3
4
5
6
7
8
9
10
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[8]; // [rsp+0h] [rbp-20h] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-18h]

v5 = __readfsqword(0x28u);
A::A((A *)v4);
A::~A((A *)v4);
return 0;
}

A::A()

1
2
3
4
5
6
void __fastcall A::A(A *this)
{
*(_DWORD *)this = 1;
*((_DWORD *)this + 1) = 2;
std::operator<<<std::char_traits<char>>(&std::cout, &unk_2005);
}

A::~A()

1
2
3
4
void __fastcall A::~A(A *this)
{
std::operator<<<std::char_traits<char>>(&std::cout, &unk_2008);
}

this指针即是堆栈上数据v4的地址。

在堆区上的普通对象

source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
class A {
public:
A() { std::cout << "A\n";}
~A() { std::cout << "~A\n";}
void test() { std::cout << "test\n";}
private:
int a = 1;
int b = 2;
};

int main() {
A *a = new A();
a->test();
return 0;
}

生成可执行文件ida反编译如下:

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
A *v3; // rbx

v3 = (A *)operator new(8uLL);
A::A(v3);
A::test(v3);
return 0;
}

A::A()

1
2
3
4
5
6
void __fastcall A::A(A *this)
{
*(_DWORD *)this = 1;
*((_DWORD *)this + 1) = 2;
std::operator<<<std::char_traits<char>>(&std::cout, &unk_2005);
}

与堆栈上的差不多, 堆栈上的对象不用管内存泄漏问题,编译器调用析构函数,开辟在堆区的对象由于没有delete a,所有没有析构函数调用和内存的释放,出现内存泄漏。

拥有vital function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
class A {
public:
A() {
std::cout << "A\n";
}
~A() {
std::cout << "~A\n";
}
virtual void vir_1() {
std::cout << "vir_1 A\n";
}
virtual void vir_2() = 0;

private:
int a;
int b;
};
class B : public A{
public:
B() {std::cout << "B\n";}
~B() {std::cout << "~B\n";}
virtual void vir_2() {
}
private:
int a = 4;
};

int main() {
B a;
return 0;
}

生成可执行文件ida反编译如下:

main

1
2
3
4
5
6
7
8
9
10
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[24]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+18h] [rbp-18h]

v5 = __readfsqword(0x28u);
B::B((B *)v4);
B::~B((B *)v4);
return 0;
}

B::B()

1
2
3
4
5
6
7
8
9
10
11
12
13
void __fastcall B::B(B *this)
{
A::A(this); //先构造父类
*(_QWORD *)this = off_3D68; // 3D68这个位置就是B类的虚函数表,将B类的虚函数表赋给this指针指向的第一个内存块
/*
.data.rel.ro:0000000000003D68 off_3D68 dq offset _ZN1A5vir_1Ev ; DATA XREF: B::B(void)+19↑o
.data.rel.ro:0000000000003D68 ; B::~B()+C↑o
.data.rel.ro:0000000000003D68 ; A::vir_1(void)
.data.rel.ro:0000000000003D70 dq offset _ZN1B5vir_2Ev ; B::vir_2(void)
*/
*((_DWORD *)this + 4) = 4;
std::operator<<<std::char_traits<char>>(&std::cout, "B\n");
}

A::A()

1
2
3
4
5
6
7
8
9
10
void __fastcall A::A(A *this)
{
*(_QWORD *)this = &off_3D88; // 3D88是A类的虚函数表
/*
.data.rel.ro:0000000000003D88 off_3D88 dq offset _ZN1A5vir_1Ev ; DATA XREF: A::A(void)+C↑o
.data.rel.ro:0000000000003D88 ; A::~A()+C↑o
.data.rel.ro:0000000000003D88 ; A::vir_1(void)
*/
std::operator<<<std::char_traits<char>>(&std::cout, &unk_2005);
}

B::~B()

1
2
3
4
5
6
void __fastcall B::~B(B *this)
{
*(_QWORD *)this = off_3D68; //析构时再次赋予虚函数表
std::operator<<<std::char_traits<char>>(&std::cout, "~B\n");
A::~A(this);
}

A::~A()

1
2
3
4
5
void __fastcall A::~A(A *this)
{
*(_QWORD *)this = &off_3D88; //析构时再次赋予虚函数表
std::operator<<<std::char_traits<char>>(&std::cout, &unk_2008);
}

由以上看类中的内容是储存在ptr + 1之后的,第一个用于储存虚函数表的地址。

ref: https://www.cnblogs.com/qg-whz/p/4909359.html

基本原则

C++98 类的基本三大原则

  1. 类的构造
  2. 类的析构
  3. 类的复制

若c++98中,不想复制的话,把构造函数设为私有变量, 并不实现构造函数
不然编译器会自动生成一个对应的构造函数

C++11 类的基本五大原则

前面三点基于c++98,增加了两个原则

  1. 5 可以对拷贝构造函数和operator=进行右值引用进行重载

也就是采用左值或者右值进行构造,编译器会对不同的构造函数进行构造.

代码例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
#include <cassert>
class RuleOfFive; //前置声明class
class Parent;
class Child {
public:
explicit Child(Parent *p) : m_parent(p) {}
private:
Parent *m_parent;
};
class Parent {
public:
// 等同于: m_children.push_back(child)
bool addChild(Child* child) {m_children.emplace_back(child); }
private:
std::vector<Child*> m_children;

};
//若c++98中,不想复制的话,把构造函数设为私有变量, 并不实现构造函数
//不然编译器会自动生成一个对应的构造函数
//c++11 中: RuleOfThree(const RuleOfThree& other) = delete;
class RuleOfThree {
public:
RuleOfThree(const char* arg) : cstring(new char[std::strlen(arg) + 1]){ //allocate
std::strcpy(cstring, arg); //populate
}
~RuleOfThree() {
delete[] cstring;
}
//c++ 11中,声明该构造不能用, 以防编译器自动生成一个构造函数
RuleOfThree(const RuleOfThree& other) = delete;
// {//copy constructor
// cstring = new char[std::strlen(other.cstring) + 1];
// std::strcpy(cstring, other.cstring);
// }
RuleOfThree& operator = (const RuleOfThree& other) {
char *tmp_cstring = new char[std::strlen(other.cstring) + 1];
std::strcpy(tmp_cstring, other.cstring);
delete[] cstring;
}
private:
char* cstring;

};
//c++11
//类的5大基本原则:
//采用左值或者右值进行构造,编译器会对不同的构造函数进行构造.
class RuleOfFive {
public:
//只能针对与右值进行构造
RuleOfFive(RuleOfFive&& rhs) {
std::cout << "右值构造.." << std::endl;
std::cout << rhs.m_value;
m_value = rhs.m_value;
rhs.m_value = nullptr;
};
RuleOfFive() : m_value(new int(10) ) {}
//采用右值或者左值进行构造
RuleOfFive(const RuleOfFive &rhs) : m_value(new int(* (rhs.m_value))) {
std::cout << "左值或者右值构造.." << std::endl;
}
RuleOfFive& operator = (const RuleOfFive &rhs) {
delete m_value; //删掉以前的
*m_value = *(rhs.m_value);
return *this;
}
//对= 操作符进行右值重载
RuleOfFive& operator=(RuleOfFive&& rhs) {
m_value = rhs.m_value;
rhs.m_value = nullptr;
return *this;
}
void print() const {
assert(m_value && "class print: "); //不推荐使用if来判断
std::cout << "value: " << *m_value << std::endl;
}
~RuleOfFive() { delete m_value; }
private:
int *m_value;
};
//左值和右值的定义:
//1. 能够取到地址的值称为左值,否则为右值
void leftOrRight() {
int a = 0;
int b;
int c = 2 + 3;
int d = a + c;
auto address = &a;
//auto address2 = &(2 + 3);
int& e = a; //左值引用
//int& e2 = 3; //错误
int&& e3 = 3; //右值引用.
}
//c++11增加了左值和右值的概念, 所以类的从3大基本原则变为了5大基本原则
void leftAndRight() {
std::vector<int> a; //left
for(int i = 0; i < 10; ++i) a.push_back(i);
auto b = a; //b as left value
auto &c = b; //c as left value
//std::vector<int>& c = b; //c as left value
//move函数啥也不干,就是让编译器把b转换为右值.
auto d = std::move(b);//std::move(b) 就为右值了
RuleOfFive f;
RuleOfFive g(f); //左值构造
RuleOfFive h (std::move(f)); //右值构造, f已经毫无意义
// f.print(); //m_value 已经为nullptr
}

int main(void) {
leftAndRight();
return 0;
}

STL

STL 是一个框架, 将数据结构和算法进一步的抽象, 包含了容器, 迭代器, 算法
容器: 储存数据的

迭代器: 容器之间统一的一个查询元素接口,可以用来遍历容器的元素

算法: 查找, 排序, 修改…

类别

1 序列式容器 array / vector / deque/list / forward_list 一般实现方式: 数组 ,指针 链表
2 关联类有序容器 set / map / multiset / multimap 实现方式: 二叉树, 红黑树
3 关联类无序容器 unordered_map / unordered_set / unordered_multimap / unordered_multiset 实现方式: hash table, 哈希表
4 其他容器: stack / queue / priority_queue / string / bitset (存储对或错)

新型: regex (正则表达式), 增强了 rand算法, thread(线程), async, furture, time

容器内元素的条件

  1. 必须可以复制(copy) 或则搬移(move) (隐含的条件是在拷贝和搬移的过程中应该没有副作用)
  2. c++ 的基本类型满足以上要求: bool char int int* char*, reference
  3. 元素必须可以被赋值操作,来作为复制或者搬移, (因为容器和算法对复写有要求)
  4. 元素可以被销毁

针对于不同的容器还有特殊的要求

  1. 对于序列式容器, 元素必须有默认的构造函数
  2. 对于某些操作,元素需要定义 ==
  3. 对于关联式容器, 排序默认准则的是 <
  4. 无顺序容器,必须要提供一个hash函数

stl容器里面存的是元素的值而不是引用,到底我们的容器里面应该存的东西是什么呢?

STL对于错误怎么处理
我们要记住的是STL的设计原则是: 效率优先, 安全为次
异常? STL自己却不怎么用

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <iostream>
#include <chrono>
#include <algorithm>
#include <list>
#include <map>
#include <random>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <deque>
//多数容器共有的接口:
template<typename T>
void containerAllInterface(T &a, T &b) {
T c;
T d(a); //copy
T e = a; //copy
T ee {a}; //copy
T eee = {a}; //copy
T f(std::move(a)); //move不是转化为右值,是移动数据
//new
auto iterB = b.begin();
auto iterE = b.end();
//old
typename T::iterator iterF = b.begin();

T g(b.begin(), b.end()); //从开始的地方到结束的地方进行拷贝
T g2(iterB, iterE); //使用迭代器方式,一样


b.size(); //std::forwad list 不提供size接口,绝大多数都有
b.empty(); //return b.size() == 0;
b.max_size();
if(b == c) {} //先比较数量, 再比较顺序
if(b != d) {} //
if(b < e) {} //unordered_set unordered_map .. 无顺序容器没有
// b <= e
// b > e
// b >= e
//赋值
e = b;
e = std::move(b);

e.swap(g); //交换容器所管理的资源, std::arrary 的交换为线性, O(n), 而其他是 O(1)
//所以其他容器交换很高效,除了array
std::swap(e, g); //与以上一样

e.cbegin(); //const_iterator, 返回const iterator的引用
auto ea = e.cbegin();
auto eea = e.cbegin();
*eea; //const &, &
*ea; // const &
e.cend();
e.clear(); //清空容器的所有元素, 有一个容器没有. std::array
//就是调用所有元素的析构.
}
void test_container() {
std::vector<int> a;
std::vector<int> b;
containerAllInterface<std::vector<int>>(a, b);

std::list<int> e;
std::list<int> f;
containerAllInterface<std::list<int>>(e, f);

std::deque<int> g;
std::deque<int> h;
containerAllInterface<std::deque<int>>(g, h);

//.......
//? array, forward_list, unordered_...
}
int main(int, char**) {
return 0;
}

vector

vector 是c++98中引入的动态数组(dynamic array)

1
2
3
4
namespace std {
template<typename T, typename Allocator = allocator<T>> //就是用默认的alloctor了,
class vector;
}

特点

随机访问元素, 末端添加删除元素效率最高, 前端和中间删除和添加元素效率最低,存在当前容器大小和容量的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <vector>
#include <iostream>
#include <cstring>
//#include <memory>
static void vectorPart() {
using Group = std::vector<float>;
Group a;
Group b = a; //拷贝构造
Group c(a); //拷贝构造
Group d(10); //定义容量的大小
Group e(10, 1.0f); //10个初始化为1.0
Group f(e.begin(), e.end()); //传入两个迭代器,中间的所有值都会被拷贝
Group g({1.0f, 2.0f, 3.0f}); //c++11, 把写好的元素直接填
Group h = {1.1f, 2.1f, 3.1f};
Group i {1.2f, 2.2f, 3.2f, 4.2f}; //initialize list

d.empty();
d.size(); //当前的大小
d.max_size(); //max_size()相对于其他容器的要小一些
d.capacity(); //最多能在装入多少个元素,前提没有分配内存的情况下.
d.reserve(100); //预先分配100个内存空间
d.shrink_to_fit(); //c++11

//operator == != < > <= >=

//赋值
b = g;
b.assign(3, 1.0f); //代表重新分配元素为: {1.0f, 1.0f, 1.0f}
b.assign(g.begin(), g.end());
b.assign({1.0f, 2.0f, 3.0f});

//交换: 交换给vector类中的指针,效率比较高
b.swap(a);
std::swap(a, b);

//元素访问:
b[0];
b.at(0); //越界抛出异常: std::out_of_range
if(b.empty()) {
b.front(); //一定要检查, 不然会出现错误, undefined
b.back();

}
b.front();
b.back();

//迭代器相关:
a.begin();
a.end();
a.cbegin();
a.cend();
a.rbegin();
a.rend();
a.crbegin();
a.crend();

//
//去除最后一个元素
//signal thread OK, multthread maybe wrong
a.pop_back(); //maybe wrong

//删除某个特定位置的元素
b.erase(b.begin()); //删除掉迭代器当前位置的元素, 而后面的元素会往前移动, 返回下一个元素的位置
b.erase(b.begin(), b.end()); //删除掉迭代器中间元素
//要检查迭代器是否失效

b.push_back(10.0f); //尾部插入元素
b.pop_back(); //获取尾部元素,且释放

//在某个位置插入元素
//在尾部插入100.0
auto iter = b.insert(b.end(), 100.0f); //返回插入元素所在的迭代器
iter = b.insert(b.end(), 10, -10.0f); //在尾部插入10个 -10.0f
b.insert(b.end(), h.begin(), h.end()); //在尾部插入 两个迭代器之间的元素

b.emplace(b.end(), 10.0f); //c++11
b.emplace_back(10.0f); //c++11 && move copy 右值 拷贝

b.resize(10); //设置vector的大小
// 若改变小了,就会调用元素相应的析构
b.resize(100, 1.0f); //把vector的大小设置为100,若元素个数不足100,以值为:1.0来填充到100
b.clear(); //注意: 并不会让以前所占的内存降低, 只是可以把空间腾出来
b.shrink_to_fit(); //c++11, 降低内存到合适的大小

//和C的接口互用
std::vector<char> carr(100, 0);
strcpy(&carr[0], "hello World\n"); //最好用 carr.data();
printf("%s", carr.data());
//错误用法:
printf("%s", carr.begin());

//异常:
//1. push_back
//2. 元素 move / copy 没有异常的话
//insert
//emplact
//emplce_back
//push_bakc
//pop_back
//erase
//swap
//clear 都不会有异常

//特殊:
//标准化的失误: 不要使用,std::vector<bool>
}
int main(int, char**) {

return 0;
}

deque

deque是c++98中引入的动态数组(dynamic array)

1
2
3
4
namespace std {
template<typename T, typename Allocator = allocator<T>>
class deque;
}

特点

随机访问元素, 末端和头部添加删除元素效率高,中间删除和添加元素效率低,而vector仅仅操作尾部效率高,元素的访问和迭代比vector要慢,迭代器不能是普通的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <deque>
#include <vector>
using Group = std::deque<float>;
static void dequePart() {
Group a;
Group b = a;
Group c(a);
Group d(10);
Group e(10, 1.0f);
Group f(e.begin(), e.end());
Group g({1.0f, 2.0f, 3.0f});
Group h = {1.0f, 2.0f, 3.0f};
Group i {1.0f, 2.0f, 3.0f};

if(!a.empty()) a.pop_back();

d.empty();
d.size();
d.max_size();

//和vector不同, deque不提供以下的函数
//d.capacity();
//d.reserve(100);
d.shrink_to_fit(); //c++11

//交换
b.swap(a);
std::swap(a, b);

//元素访问
b[0];
b.at(0);
b.front();
b.back();

//迭代器相关
a.begin();
b.end();
a.cbegin();
a.cend();
a.rbegin();
a.rend();
a.crbegin();
a.crend();


a.pop_back();
a.push_back(1.0f);
a.push_front(1.2f); //vector没有
b.emplace_front(1.0f);


auto iter = b.insert(b.end(), 100.0f); //在某个位置插入元素
b.insert(b.end(), 10, -10.0f);
b.insert(b.end(), h.begin(), h.end());
b.emplace(b.end(), 10.0f);
b.emplace_back(10.0f);
b.resize(10);
b.resize(100, 10.0f);
b.clear();
b.shrink_to_fit(); //释放适当的内存

//异常
//1 push_back push_front 不会抛出异常
//2 move /copy 没有异常的话
//insert emplace emplace_back push_back emplace_front
//pop_back pop_front erase swap clear 不会抛出异常

//应用场景: 可以来储存网络消息的连聊天包机制,分别发包机制
//using Buffer = std::vector<char>;
//using BufferGroup = std::deque<Buffer>;
//BufferGroup group;
//Buffer buffer;
//
//auto ok = readFromClinet(socket, &buffer);
//if(ok) {
// group.emplace_back(std::move(buffer));
//}else {
// //handle error
//}
//while(!group.empty()) {
// auto ok = sendToClient(socket, group.front());
// if(ok) {
// group.pop_front();
// }else {
// //handle error
// }
//}

}
int main(void) {

return 0;
}

list

list 是c++98中引入的双向串列(double linked list)

1
2
3
4
namespace std {
template<typename T, typename Allocator = allocator<T>>
class list;
}

特点

不支持随机访问元素,访问头部和尾部元素快
任何位置插入删除元素都很快,常亮时间内完成
插入和删除不会造成迭代器的失效
对于异常支持好, 出现异常对于list而言, 要么不成功, 要么没有影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <list>
#include <cassert>
#include <iostream>
static void listPart() {
using Group = std::list<float>;

Group a;
Group b = a;
Group c(a);
Group d(10);
Group e(10, 1.0f);
Group f(e.begin(), e.end());
Group g({1.0f, 2.0f, 3.0f});
Group h = {1.0f, 2.0f, 3.0f};
Group i {1.0f, 2.0f, 3.0f};

d.empty();
d.size();
d.max_size();
//和vecotr不同, list不提供以下函数
//d.capacity();
//d.reserve(100);
//d.shrink_to_fit();

// operator == != < > <= >=
//赋值
b = g;
b.assign(3, 1.0f);
b.assign(g.begin(), g.end());
b.assign({1.0f, 2.0f, 3.0f});

//交换
b.swap(a);
std::swap(a, b);

//元素访问, 不能随机访问
//b[0];
//b.at(0);
b.front();
b.back();

//迭代器相关:
a.begin();
a.end();
a.cbegin();
a.cend();
a.rbegin();
a.rend();
a.crbegin();
a.crend();

a.resize(10);
auto iterBegin = a.begin();
assert(a.size() >= 10);
//for(int i = 0; i < 5; ++i) ++iterBegin; //改变迭代器
std::advance(iterBegin, 0); //改变迭代器位置2
//遍历元素
for(; iterBegin != a.end(); ++iterBegin) std::cout << *iterBegin << " ";

a.pop_back(); //maybe wrong
if(!a.empty()) a.pop_back();

b.erase(b.begin());
b.erase(b.begin(), b.end());

b.push_back(10.0f);
b.pop_back();
b.push_front(1.2f);
b.emplace_front(1.3f);

auto iter = b.insert(b.end(), 100.0f);
iter = b.insert(b.end(), 10, -10.0f);

b.resize(5);
b.resize(10, 1.2f);

//算法:
b.remove(1.0f); //删除值为1.0f的所有值
//符合某个条件的值把它删除掉, 100.0f的值全部删除掉
b.remove_if([](auto v) { return v > 100.0f; }); //v 是list中元素的值
b.reverse(); //反转list
b.sort(); //默认以 <

b.merge(g); // 合并两个排好顺序的list在b中,并清空g中的元素,结果b中就也是排好序的
c.unique(); // 前提: 将排好序的list重复的元素去掉
c.splice(c.begin(), b); //在某个位置插入整块的list, 把b作为一块插入c.begin().

}
int main(void) {
listPart();
return 0;
}

forward_list

//forward_list 是c++11中引入的单项串列(singal linked list)

1
2
3
4
namespace std {
template <typename T, typename Allocator = allocator<T>>
class forward_list;
}

特点

不支持随机元素访问, 访问头部元素速度快,”forward_list” 和自己手写的c-style signal linked list相比没有任何时间和空间上的额外开销, 任何性质如果和这个目标抵触,我们就放弃该特征.任何位置插入和删除元素都很快, 常亮时间完成。插入和删除不会造成迭代器失效,对于异常支持好, 出现异常对于forward_lit而言, 要么不成功, 要么没什么影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <forward_list>
#include <iostream>
static void forwardListPart() {
using Group = std::forward_list<float>;

Group a;
Group b = a;
Group c(a);
Group d(10);
Group e(10, 1.0f);
Group f(e.begin(), e.end());
Group g({1.0f, 2.0f, 3.0f});
Group h = {1.0f, 2.0f, 3.0f};
Group i {1.0f, 2.0f, 3.0f};

d.empty();
//d.size(); //没有size() ,为了效率
d.max_size();
//和vector不同, forward_list不提供以下函数:
//d.capacity();
//d.reserve(100);
//d.shrink_to_fit();

//operator == != < > <= >=

//赋值
b = g;
b.assign(3, 1.0f);
b.assign(g.begin(), g.end());
b.assign({1.0f, 2.0f, 3.0f});

//交换:
b.swap(a);
std::swap(a, b);

//元素访问, 不能随机访问
//b[0];
//b.at(0);
b.front();
//b.back(); 没有提供最后一个

//迭代器相关
a.begin();
a.end();
a.cbegin();
a.cend();
a.before_begin(); //返回第一个元素的前一个位置, 这个位置只是个占位符
a.cbefore_begin(); //const &
//a.rbegin();
//a.rend();
//a.rcbegin();
//a.rcend();

auto iterBegin = a.begin();

//a.pop_back();
a.empty();

//b.erase(b.begin()); //不支持erase
b.erase_after(b.begin()); //删除b.begin(), 的下一个元素
b.erase_after(b.before_begin()); //return void //正确删除第一个元素
b.erase_after(b.begin(), b.end()); //return void

//b.push_back(10.0f)
//b.pop_back();

b.push_front(1.2f);
b.emplace_front(1.3f);

//b.insert(b.begin(), 3.0f); 不支持insert

auto iter = b.insert_after(b.before_begin(), 100.0f);
iter = b.insert_after(b.before_begin(), 10, -10.0f);
b.insert_after(b.before_begin(), h.begin(), h.end());

//b.emplace(b.end(), 10.0f);
//b.emplace_back(10.0f);
b.resize(10);
b.resize(100, 1.0f);
//算法:
b.remove(1.0f);
b.remove_if([](auto v) { return v > 100.0f; });
//b.reverse(); //1 2 3 4 -> 4 3 2 1
//std::sort(a.begin(), a.end());
b.sort(); // <
g.sort();
b.merge(g); //对于排好顺序的list进行合并,且合并后也是排好顺序的
c.unique(); //对于排好序的进行删除重复元素
c.splice_after(c.before_begin(), b);


}
int main(void) {
//例:在索引为3的位置之前插入一个元素
std::forward_list<int> forlist = {1, 2, 3, 4, 5, 6};
auto fiter = forlist.before_begin();
for(int i = 0; i < 2; ++i, ++fiter);
forlist.insert_after(fiter, 10);
for(fiter = forlist.begin(); fiter != forlist.end(); ++fiter) std::cout << *fiter << " " ;
std::cout << std::endl;

return 0;
}

array

array 实际上是对c/c++语言中的原生数组进行了封装

1
2
3
4
namespace std {
template<typename T, size_t N>
class array;
}

特点

内存分配在栈(stack), 绝不会重写分配.随机访问元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <array>
#include <cstring>

//c++ 11
template<typename C>
void checkSize(C &c) {
if(c.size() > 3) {
c[3] = 10; //单线程OK,多线程可能出错
}
c.at(3) = 10;
}

void arrayPart() {
std::array<int, 100> a; //不进行初始化, 只管分配内存
std::array<int, 100> b = {}; //初始化为 0
//以上两者的区别.

std::array<int, 5> obj = {1, 2, 3, 4, 5};
std::array<int, 5> obj2 = {1}; //第一个为1, 后面不0

//接口
a.empty(); //never be true if size > 0
a.size();
a.max_size();

//operator == < != > <= >=
auto aa = a;
aa.swap(a); //array的swap是交换每一个元素都需要做交换,因为它是在stack中储存的.不是通过堆来管理
//访问元素
a[1]; //不进行检查,越界出错
a.at(1); //进行检查,越界抛出异常
a.front(); //返回头部元素的引用
a.back(); //返回最后元素的引用
checkSize(a);

//迭代器相关的:
a.begin(); //可以对begin减引操作, 但不能堆end进行此操作
a.end();
a.cbegin(); //const 引用
a.cend();

a.rbegin(); //从尾部遍历到头部,与以上相反
a.rend();

a.crbegin(); //const 方式从尾部遍历到头部
a.crend();


//和C的接口互用
std::array<char, 100> carr;
strcpy(&carr[0], "hello world\n");
printf("%s", &carr[0]); //更好的使用 carr.data();
printf("%s", carr.data());
//错误用法
printf("%s", carr.begin());


//特殊的地方
auto info = std::get<1>(carr); //与 carr[1] 一样
printf("%c\n", info); //
carr.fill(0); //对所有元素赋予0

//异常 exception
//c.at(pos)
//copy move swap, 进行操作时,可能触发异常

}
int main(void) {
arrayPart();
return 0;
}

C++中的补码公式与位域

补码公式

-x = x+1 = ~(x-1)
~x = -x-1
-(
x) = x+1
~(-x) = x-1

x+y = x - y-1 = (x|y) + (x&y)
x-y = x + ~y+1 = (x|
y) - (x&y)
x^y = (x|y) - (x&y)
x|y = (x& ~y) + y
x&y = (
x|y) - ~x

x==y : (x-y|y-x)
x!=y : x-y|y-x
x<y : (x-y) ^ ((x^y) & ((x-y) ^x))
x<=y : (x|
y) & ((x^y) | (y-x))
x<y : (
x&y) | ((x|y) & (x-y)) //unsigned
x<=y : (
x|y) & ((x^y) | ~(y-x)) //unsigned

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
using namespace std;

void operator_1(void);
void operator_2(void);
void operator_3(void);
int main(void) {

operator_1();
operator_2();
operator_3();
return 0;
}
void operator_1(void) {
int x = 1;
cout << "-x = " << -x << " ~x+1 = " << ~x + 1 << endl;
cout << "~x = " << ~x << " -x-1 = " << -x - 1 << endl;
cout << "-(~x) = "<< -(~x) << " x+1 = " << x + 1 << endl;
cout << "-(~x) = "<< -(~x) << " x-1 = " << -x -1 << endl;
}
void operator_2(void) {
int x = 3, y = 5;
cout << endl;
cout << "x: " << x << "y: " << y << endl;
cout << "x+y=" << x+y << " x- ~y-1= " << x - ~y - 1
<< " (x|y)+(x&y) = " << (x|y) + (x&y) << endl;
cout << "x-y=" << x-y << " x+ ~y+1= " << x + ~y + 1
<< " (x|~y) - (~x&y) = " << (x|~y) - (~x|y) << endl;

cout << "x^y=" << (x^y) << " (x|y) - (x&y)" << (x|y) - (x&y) << endl;
cout << "x|y=" << (x|y) << " (x& ~y) + y = " << (x& ~y) + y << endl;
cout << "x&y=" << (x&y) << " (~x|y) - ~x = " << (~x|y) - ~x << endl;
}
void operator_3(void) {
int x = -3, y = -5;
unsigned int x_t = -1, y_t = -5;
cout << "x: " << x << "y: " << y << endl;
cout << "x==y =" << (x==y) << " ~(x-y|y-x) =" << ~(x-y|y-x) << endl;
cout << "x!=y =" << (x!=y) << " x-y|y-x =" << (x-y|y-x) << endl;
cout << "x<y =" << (x<y) << " (x-y) ^ ((x^y) & ((x-y)^x))=" << ((x-y) ^ ((x^y) & ((x-y)^x))) << endl;
cout << "x<=y =" << (x<=y) << " (x|~y) & ((x^y) |~ (y-x))=" << ((x|~y) & ((x^y) |~ (y-x))) << endl;
cout << "unsigned: x<y =" << (x_t<y_t)
<< " (~x&y) | ((~x|y) & (x-y))" << ((~x_t&y_t) | ((~x_t|y_t) & (x_t-y_t))) << endl;

cout << "unsigned: x<=y =" << (x_t<=y_t)
<< " (~x|y) & ((x^y) | ~(y-x) =" << ((~x_t|y_t) & ((x_t^y_t) |~ (y_t-x_t))) << endl;

}

位域

在结构内声明位域的形式如下:

1
2
3
4
struct
{
type [member_name] : width ;
};

下面是有关位域中变量元素的描述:

元素 描述
type 只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。
member_name 位域的名称。
width 位域中位的数量。宽度必须小于或等于指定类型的位宽度。

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
typedef struct bs {
int a:2;
int b:1;
int c:12;
unsigned d:4
}bs;
struct bit_2 {
unsigned a:2;
unsigned :2; //It can't use
unsigned :0; //NULL
unsigned b:4;
unsigned c:4;

};
int main(void) {
bs bit;
printf("sizeof(%d) \n",sizeof(struct bs));
bit.a = 4;
bit.b = 1;
bit.c = 34235;
printf("a: %d b: %d c: %d\n", bit.a, bit.b, bit.c);

return 0;
}

lambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <functional>
#include <iostream>
//基础函数
void printInfo(int a, int b, int c) {
std::cout << " a " << a << " b " << b << " c " << c << std::endl;
}

//操作符重载函数
struct Print {
void operator() (int a, int b, int c) const {
std::cout << " a " << a << " b " << b << " c " << c << std::endl;
}
};
//模板函数
template<typename T1, typename T2, typename T3>
void templatePrint(T1 a, T2 b, T3 c) {
std::cout << " a " << a << " b " << b << " c " << c << std::endl;
}

//操作符重载 + 模板 的函数
struct TemplatePrint {
template<typename T1, typename T2, typename T3>
void operator() (T1 a, T2 b, T3 c) const {
std::cout << " a " << a << " b " << b << " c " << c << std::endl;
}
};

inline void print(int a, int b, int c) {
std::cout << " a " << a << " b " << b << " c " << c << std::endl;
}
template<typename Fun>
void printUseFun(Fun fun, int a, int b, int c) {
fun(a, b, c);
}
void test_1() {
Print printUseClass;
TemplatePrint printUseTempClass;

printInfo(1, 2, 3);
printUseClass(1, 2, 3); //operator() (int a, int b, int c)

templatePrint(1, 2, 3);
printUseTempClass(1, 2, 3);
print(1, 2, 3);

//以前inline 函数跟普通函数调用差不多
//lambda就是个inline函数, 只是把 inline 函数作为一个参数或者一个local变量来使用,

std::cout << "lamda: " << std::endl;
auto local = [](int a, int b, int c) {
std::cout << " a " << a << " b " << b << " c " << c << std::endl;
};
printUseFun(local, 1, 2, 3);
printUseFun([](int a, int b, int c) {
std::cout << "lamda2:" << std::endl;
std::cout << " a = " << a << " b = " << b << " c = " << c << std::endl;
}, 2, 3, 5);

int a = 6, b = 7, c = 8;

auto local2 = [a, b, c]() {
std::cout << " a = " << a << " b = " << b << " c = " << c << std::endl;
};
local2();

auto local3 = [=]() {
std::cout << "====" << std::endl;
std::cout << " a = " << a << " b = " << b << " c = " << c << std::endl;
};
local3();

auto local4 = [=]() mutable {
std::cout << "====" << std::endl;
a = 4, b = 4 , c = 4;
std::cout << " a = " << a << " b = " << b << " c = " << c << std::endl;
};
local4();

}
int main(void) {
test_1();
return 0;
}

C++11新型for循环,auto, 类成员的初始化及左右值引用

C++11中新型for循环,auto, 类成员的初始化及左右值引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#include <typeinfo>
#include <iostream>
#include <string>
#include <vector>
#include <iterator>

static void autoValue();
static void autoPointer();
static void newVersionFor();
static void newVersionConstruct();
static void defaultInitValue();
static void leftRefAndRightRef();

//自定义类实现新型for循环
class MyVector {
public:
using GroupType = std::vector<int>;
public:
GroupType m_group;
void push_back(int x) {
m_group.push_back(x);
}
};

MyVector::GroupType::iterator begin(MyVector &v) {
return v.m_group.begin();
}
MyVector::GroupType::iterator end(MyVector &v) {
return v.m_group.end();
}
void testMyClass() {

MyVector mv;
mv.push_back(1);
mv.push_back(2);
mv.push_back(3);
for(auto i : mv) {
std::cout << i << " ";
}
std::cout << std::endl;
}


int main(int /*argc*/, char** /*argv*/) {
testMyClass();
//new version auto
//autoValue();
//autoPointer();
// newVersionFor();
//newVersionConstruct();
// defaultInitValue();
leftRefAndRightRef();
return 0;
}

static void autoValue() {
auto age = 10;
auto name = std::string("Yt");
auto height = 160.0f;
auto wight = 72.0;
std::cout << "age is type " << typeid(age).name() << std::endl;
std::cout << "name is type " << typeid(name).name() << std::endl;
std::cout << "height is type " << typeid(height).name() << std::endl;
std::cout << "weight is type " << typeid(wight).name() << std::endl;
}
static void autoPointer() {
auto age = new int(10);
auto name = "Yt";
auto height = new float(160.0f);
auto wight = new double(72.0);

std::cout << "age is type " << typeid(age).name() << std::endl;
std::cout << "name is type " << typeid(name).name() << std::endl;
std::cout << "height is type " << typeid(height).name() << std::endl;
std::cout << "weight is type " << typeid(wight).name() << std::endl;
}
static void newVersionFor() {
int ids[] = {1, 2, 3, 4, 5};
std::cout << "new version";
for(auto v : ids) {
std::cout << v << " ";
}
std::cout << std::endl;

std::cout << "old version";

for(int i = 0; i < sizeof(ids) / sizeof(ids[0]); ++i) {
std::cout << ids[i] << " ";
}
std::cout << std::endl;

//vector::
std::vector<int> group;
for(int i = 0; i < 4; ++i) group.push_back(i);
//old version:
//老版本遍历方式
for(std::vector<int>::size_type i = 0, size = group.size(); i < size; ++i) {
//cout << group[i] << " ";
}
//通过迭代器来遍历
std::cout << "iterator: " << std::endl;
//-------对于迭代器自增的时候,要用前置操作符,不要用后置操作符. ++iter, 若使用后置操作符号
//多了一个步骤,就是把自增前的值先保留起来, 然后在自增.无用功
for(std::vector<int>::const_iterator iter = group.begin(); iter != group.end(); ++iter) {
std::cout << *iter << " ";
}

std::cout << std::endl;
//auto version
std::cout << "vector old version:" << std::endl;
for(auto v : group) {
std::cout << v << " ";
}

std::cout << std::endl;
std::cout << "vector new version:" << std::endl;
for(auto &v : group) {
v = 1;
}
for(const auto &v : group) {
std::cout << v << " ";
}
}

class A {
public:
//why explicit ?? 具体含义是什么?
explicit A(int value) : m_value(value) { }

private:
int m_value;
};

static void newVersionConstruct() {

A a(10);
A b{3};

//old
int avector[] = {1, 2, 3};
std::vector<int> bv;
for(auto v : avector) bv.push_back(v);

//new
std::vector<int> cv = {1, 2, 3};
std::vector<int> v {1, 2, 3};

A c(true);
A d(false);

A e(1.0);
// A f{1.0} //不能做构造, 只能提高精度, 不能丢失
}

class B {
public:
B() : m_age(18), m_height(170), m_weight(75) {}
explicit B(int age) : m_age(age), m_height(170), m_weight(75) {}
B(int age, int height) : m_age(age), m_height(height) {}
int age() const { return m_age; }
int height() const { return m_height; }
int weight() const { return m_weight; }
private:
int m_age;
int m_height;
int m_weight;
};

class NewB {
public:
NewB() {}
explicit NewB(int age) : m_age {age} {}
NewB(int age, int height) : m_age{age}, m_height {height} {}
int age() const { return m_age; }
int height() const { return m_height; }
int weight() const { return m_weight; }
void p() const { std::cout << m_value << " " << m_fightValue;}

private:
int m_age = 18; //初始化, 放置出现bug
int m_height = 170;
int m_weight = 75;
int m_value{}; //代表拿int的默认值来作初始化,也就是0
double m_fightValue{}; //0.0
};

static void defaultInitValue() {
B a(10, 20);
NewB b(10, 20);

std::cout << "Old a age is" << a.age() << "height " << a.height()
<< " wight " << a.weight() << std::endl;

std::cout << "New b age is" << b.age() << "height " << b.height()
<< " wight " << b.weight() << std::endl;

b.p();
}

static void leftRefAndRightRef() {
//什么是引用
//引用类似与指针, 不同之处在于, 指针可以为nullptr, 但引用不可以
int a = 10;
int &refA = a;
const int &constRefA = a;
std::cout << " a" << a << " ref of a " << refA << " const ref a "
<< constRefA << std::endl;
//this is a error
// int &refB = 10;
const int &constRefB = 10; //不管左值还是右值,都能赋给const 引用
std::cout << "different version const ref " << constRefB << std::endl;

//auto &&rrB = 20; //与下一样
int &&rrB = 20;
const int &&crrB = 20;

rrB = 30; //可以改变右值, 其实只是改变那个值而已
std::cout << " right ref of b " << rrB << " const right ref b "
<< crrB << std::endl;
int b = 20;
int &&newRRB = std::move(b); //强行转为右值
const int &&cNewRRB = std::move(b);
std::cout << " b " << b << " right ref of be " << newRRB << "const right ref b "
<< cNewRRB << std::endl;
std::cout << "address " << &b << " ref " << &newRRB << " c ref " << &cNewRRB << std::endl;

}

C++11智能指针

目前c++ 的4种指针

  1. auto_ptr c++98 智能指针,使用了

  2. shared_ptr //共享指针

  3. unique_ptr //只能由一个使用者使用

  4. weaked_ptr //与share_ptr搭配使用

shared_patr

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <iostream>
#include <memory>
static void interfaceOfSharedPtr();

int main(void) {
interfaceOfSharedPtr();
return 0;
}

class Object {
public:
Object(int id) : m_id(id) { std::cout << "init obj " << m_id << std::endl; };
~Object() { std::cout << "bye bye " << m_id << std::endl; }
int id() const { return m_id; }
private:
int m_id = 0;
};


//使用智能指针
using ObjectPtr = std::shared_ptr<Object>;


//作为参数的obj,相当于拷贝了一次,use_count 会+ 1,但当函数结束时候,智能指针析构,use_count会恢复原来的个数
void print(ObjectPtr obj) {
//-------------------------智能指针的成员函数
std::cout << "count " << obj.use_count() << " id " << obj->id() << std::endl;
}
//以引用方式,不会进行copy, use_count不变
void printRef(const ObjectPtr &obj) {
std::cout << "ref count " << obj.use_count() << "id " << obj->id() << std::endl;
}


static void interfaceOfSharedPtr() {
ObjectPtr null;
std::cout << "ref count is " << null.use_count() << std::endl;

//使用智能指针来储存对象的指针
ObjectPtr obj (new Object(1));
std::cout << "ref count is " << obj.use_count() << std::endl;

ObjectPtr obj2(obj);
std::cout << "ref count is " << obj2.use_count() << std::endl;

ObjectPtr obj3 = obj;
std::cout << "ref count is " << obj.use_count() << std::endl;

obj2.reset(); //What dose means of reset ?
std::cout << "ref count is " << obj.use_count() << std::endl;

ObjectPtr obj4;
obj3.swap(obj4); //把obj3的管理资源相互交换
std::cout << "obj4 ref count is " << obj4.use_count() << std::endl;
std::swap(obj3, obj4); //采用另一种方式交换
std::cout << "obj4 ref count is " << obj4.use_count() << std::endl;

//把智能指针存储的指针传出来
auto p = obj.get();
if(p) {
std::cout << "id is" << p->id() << std::endl;
}
if(obj) { //重载了 operator bool
std::cout << "p id is " << obj->id() << std::endl; //重载: operator ->
std::cout << "ref id is " << (*obj).id() << std::endl; //重载了: operator *
}

obj4 = nullptr;
//----------------------------->obj.use_count() == 1 可能效率比较底一点
std::cout << "only one hold ptr " << obj.unique() << std::endl; //判断是否是一个人在使用
std::cout << "judge differnt: " << std::endl;
std::cout << "use_count: " << obj.use_count() << std::endl;
printRef(obj);
print(obj);
std::cout << "use_count: " << obj.use_count() << std::endl;
}

void deleterOfObject(Object *obj) {
if(obj)
std::cout << "delete obj " << obj->id() << std::endl;
delete obj;
}

void useDeleter() {
//当智能指针出了作用域后,析构函数就被调用, 哪怕中途抛出异常
ObjectPtr obj(new Object(2), deleterOfObject);
ObjectPtr obj2 = obj;
}

weak_ptr

c++智能指针-shared_ptr的尴尬-诞生weak_ptr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <iostream>
#include <memory>
#include <cassert>

//auto ptr
//shared_ptr
//unique_ptr
//weak_ptr

class Parent; //采用前置声明

using ParentPtr = std::shared_ptr<Parent>;
typedef std::weak_ptr<Parent> WeakParentPtr;

class Child {
public:
// ParentPtr father; //内存泄露
WeakParentPtr father; //采用弱指针,则可以释放内存,不造成内存泄露
Child() { std::cout << "Child!!" << std::endl; }
~Child();
};

typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr;

class Parent {
public:
Parent() { std::cout << "Parent!!" << std::endl; }
ChildPtr son;
// WeakChildPtr son;

void print() const { std::cout << "use_count: " << this->son.use_count() << std::endl;}
~Parent();
};

class Object {
public:
Object(int id) : m_id(id) { std::cout << "init obj " << m_id << std::endl; };
~Object() { std::cout << "bye bye " << m_id << std::endl; }
int id() const { return m_id; }
private:
int m_id = 0;
};
using ObjectPtr = std::shared_ptr<Object>;

Child::~Child() { std::cout << "bye child!" << std::endl; }
Parent::~Parent() { std::cout << "bye parent!" << std::endl; }
//----------------------------------------------
void testParentAndChild() {
ParentPtr p(new Parent());
ChildPtr c(new Child());

//采用这种方式,造成内存泄露
p->son = c; //智能指针拷贝, use_count + 1, c 的use_count 为 2
p->print();
c->father = p; //p 的 use_count 也为 2 了

//智能指针析构之后use_count -1
//智能指针特性: 只有当use_count 变为0 时,才能释放掉管理的资源
//所以智能指针析构后,use_count 不为0,则造成了内存泄露
////////////
//只要打破环状的引用,则share_ptr就会很好的管理内存
}
//----------------------------------------------
//为了解决以上问题, 采用weak_ptr来解决
void sharedPtrWithWeakPtr() {
ObjectPtr obj(new Object(1));
typedef std::weak_ptr<Object> WeakObjectPtr;
WeakObjectPtr weakObj(obj);//弱指针依赖于share_ptr,若share_ptr是有效的,则wake_ptr也是有效的.
WeakObjectPtr weakObj2(obj);
//而wake_ptr在进行赋值的操作运算的时候,并不影响use_count, 相当于只是作为监听者


std::cout << "obj use count is " << obj.use_count() << std::endl;
{
auto p = weakObj.lock(); //相当于返回了一个 ObjectPtr类型,
std::cout << "expired: " << weakObj.expired() << std::endl; //expierd() 查看监控的资源过期没有过期
if(p) {
std::cout << p.unique() << std::endl; //false, use_count >= 2, 因为auto p = ...,进行了一次指针拷贝,
//所以use_count + 1, 而weakObj.lock()返回若是有效的话,则use_count >= 1, 否则返回nullptr
//则总体的就use_count >= 2
//do what you wanna do
}else {


}
}
obj.reset(); //obj 放弃管理的资源
std::cout << "endl;";
auto p2 = weakObj.lock(); //返回空,因为,obj不管理资源了

obj.reset(new Object(2)); //重新管理另一个资源
{
auto p = weakObj.lock();
if(p) {
assert(false);
}else {
//要么资源已经释放,要么obj管理的资源跟换了.
std::cout << "changed!" << std::endl;
}

}
std::cout << "expired: " << weakObj.expired() << std::endl; //expierd() 查看监控的资源过期没有过期
}
int main(void) {
testParentAndChild();
// sharedPtrWithWeakPtr();
return 0;
}

shared_ptr储存this指针多次析构问题

enable_shared_from_this解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <iostream>
#include <memory>
#include <cassert>
/*
//this //调用了两次析构函数,
ParentPtr p(this);
//智能指针出了作用域后就delete this ,析构了
//所以错了,为了解决这个问题,就需要自身的类继承于 std::enable_shared_from_this<Type>
//将ParentPtr p(this) 换成, shared_from_this();

*/
class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr;

class Child : public std::enable_shared_from_this<Child> {
public:
WeakParentPtr father;
// ParentPtr father;
~Child();
Child();
void checkRelation();
};

typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr;

class Parent : public std::enable_shared_from_this<Parent> {
public:
WeakChildPtr son;
// ChildPtr son;
~Parent();
Parent();
void checkRelation();
};

void handleChildAndParentRef(const Parent &p, const Child &c) {
if(c.father.lock().get() == &p && p.son.lock().get() == &c) {
std::cout << "right relation" << std::endl;
}else {
std::cout << "oop!!!!!!" << std::endl;
}
}
void handleChildAndParent(const ParentPtr &p, const ChildPtr &c) {
assert(c);
assert(p);
if(c->father.lock() == p && p->son.lock() == c) {
std::cout << "right relation" << std::endl;
}else {
std::cout << "oop!!!!!!" << std::endl;
}
}
Child::Child() { std::cout << "hello child" << std::endl; }
Child::~Child() { std::cout << "bye child" << std::endl; }
Parent::Parent() { std::cout << "hello parent" << std::endl; }
Parent::~Parent() { std::cout << "bye parent" << std::endl; }

void Parent::checkRelation() {
auto ps = son.lock();
if(ps) {
//this //调用了两次析构函数,
// ParentPtr p(this);
//handleChildAndParent(p, ps);

//智能指针出了作用域后就delete this ,析构了
//所以错了,为了解决这个问题,就需要自身的类继承于 std::enable_shared_from_this<Type>
//将ParentPtr p(this) 这种错误形式换成, shared_from_this()
handleChildAndParent(shared_from_this(), ps);
}
std::cout << "after call checkRelation" << std::endl;
}

void Child::checkRelation() {

}
void testParentAndChild() {
//若Parent 继承于 std::enable_shared_from_this<Type>
//则 Parent pp; 这种静态的就不推荐
ParentPtr p(new Parent());
ChildPtr c(new Child());
p->son = c;
c->father = p;
p->checkRelation();
}

static void interfaceOfSharedPtr();
static void sharedPtrWithWeakPtr();
static void uniquePtr();

int main(void) {
testParentAndChild();
return 0 ;
}

unique_ptr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <memory>
#include <cassert>

class Object;

typedef std::unique_ptr<Object> UniqueObjectPtr;
using ObjectPtr = std::shared_ptr<Object>;

void print(const UniqueObjectPtr& obj) {}

class Object {
public:
Object(int x) : m_id(x) { std::cout << "Hello Obj" << std::endl; };
~Object() { std::cout << "Bye Obj" << std::endl; };
int id() { return m_id; }
private:
int m_id = 0;

};
void transfer(UniqueObjectPtr obj) {
std::cout << obj->id() << std::endl;
}

void uniquePtr() {
UniqueObjectPtr obj { new Object(1) };
auto p = obj.get(); //operator bool
if(p) {
//do some
}
//better
if(obj) {

}

std::cout << p->id() << " " << obj->id() << " " << (*obj).id() << " " << std::endl;
print(obj);

p = obj.release(); //释放所管理的指针
std::cout << "release: unique_ptr" << std::endl;
delete p; //直接释放资源

obj.reset(new Object(2)); //reset 就是将以前所管理的指针释放掉, 管理一个新的指针
//UniqueObjectPtr (const UniqueObject&) = delete //这种拷贝构造函数不存在
//UniqueObjectPtr (const UniqueObject&&) = default //
transfer(std::move(obj)); //采用这种方式传入, 则自己的资源不在管理,而交给了这个参数
//传入之后, obj 值为null

assert(obj == nullptr);
// std::cout << obj->id() << std::endl;

obj.reset(new Object(3));
ObjectPtr sharedObj(std::move(obj));
assert(obj == nullptr);
//boost库
/*
c++ 智能指针来源于boost库.
shared_ptr
weak_ptr
enable_shared_from_this
scoped_ptr //与unique_ptr 很类似, 但局限性比较多, 所以c++11没有采用
unique_ptr
*/
}

int main(void) {
uniquePtr();
return 0;
}

智能指针总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <memory>
#include <cassert>
void sharedPtrNotice();
class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr;
class Child : public std::enable_shared_from_this<Child> {
public:
WeakParentPtr father;
Child();
~Child();
};
class Object {
public:
Object(int x) : m_value(x) { }
private:
int m_value = 0;
};
using ObjectPtr = std::shared_ptr<Object>;

void sharedPtrNotice() {
//1 : 绝对不要自己手动的管理资源
//int* a = new int(10);
//delete a;
//int *b = malloc(sizeof(int));
//if(b) free(b);

//2 :一个裸的指针不要用两个shared_ptr管理, 对于unique_ptr也如此
//auto pObj = new Object(1);
//ObjectPtr obj(pObj);
//ObjectPtr obj2(pObj);

//用weak_ptr打破循环引用,parent 和 child
//当需要在类的内部接口中, 如果需要将this 作为智能指针来使用的话,
//需要用该类派生自std::enable_shared_from_this
//
//使用share_ptr作为函数的接口,如果有可能有const shared_ptr& 的形式
//多线程模式下使用shared_ptr需注意的事项(....)

//shared_ptr weak_ptr 和裸指针相比, 会大很多, 并且效率上会有影响.
//尤其是在多线程模式下

//shared_ptr 和 weak_ptr彼此互存,智能指针,空间上消耗资源比较大,而且时间效率上也比较低

//一般情况下 ObejectPtr obj(new Object(2)), 相当于进行了两次new的过程.对于时间和空间上比较消耗
//官方发现了这个问题,做了如下改正,且实现的功能一样

//只实现了一次new,将new Object() 和 new 自身的指针归在一起
ObjectPtr obj5 = std::make_shared<Object>(3); //真正推荐使用...


//enable_shared_from_this 中shared_from_this()和构造析构函数一样
//不能在构造或者析构中使用,否则会出错的.

//某些情况下,会出现内存不会降问题.尤其是使用weak_ptr来处理循环引用问题
//那是可能因为weak_ptr给勾住了,也要需要weak_ptr释放了,才能解决.

//如果有可能,优先使用类的实例,其次万不得已 2 使用std::unique_ptr
//万不得已 3 使用 std::shared_ptr
Object obj6(4); //优先
std::unique_ptr<Object> puObj(new Object(1)); //其次
std::shared_ptr<Object> psObj = std::make_shared<Object>(3); //在其次

}
int main(void) {

return 0;
}

打赏点小钱
支付宝 | Alipay
微信 | WeChat