C++

新标准

c++

Posted by Jow on September 12, 2019

目录

  1. 新的标准
  2. 移动语义和右值引用
  3. 委托构造函数
  4. 继承构造函数
  5. 管理虚方法:override和final

Study hard, work hard.

流和缓冲区

c++11新增了用大括号括起来的列表的适用范围,使其可以用于所有的内置类型和用户定义的类型(即类对象)。适用初始化列表时,可添加等号,也可以不添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int x = {5};
int y {5};
short quar[5] {1,2,3,4,5};
int *ar = new int [4] {2,3,4,5};

class Strump{
private:
	int roots;
	double weight;
public:
	Strump(int r, double w):roots(r),weight(w){}
}
Strump s1(1,1.1);
Strunp s2{2,2.2};
Strump s3 = {3,3.3};

在声明方面,新增了auto类型,能够实现自动类型推断。decltype将变量的类型声明为表达式指定的类型。decltype(x) y.y的类型和x一致。

返回类型后置 auto f2(double, int) -> double.有点像labmda函数.

1
2
3
[]()return ->int{return 1}

移动语义和右值引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
vector<string> vstr;
...
vector<string> sdtr_copy1(vstr>;

vector<string> allcaps(const vector<string> & vs){
	vector<string> temp;
	//code that stores an all-uppercase version if vs in temp
	return temp;
}

vector<string> str;
//build up a vector of 20000 strings,each if 1000 characters
vector<string> str_copy1(str);//#1
vector<string> str_copy2(allcaps(str));//#2

从表面上看,语句#1和#2类似,他们都是用一个现有的对象初始化一个vector对象,如果深入探索这些代码,将发现allcaps()创建了对象temp,该对象管理20000000个字符;vector和string的复制构造函数创建者20000000个字符的副本,然后程序删除allcaps()返回的临时对象。这里的要点是做了大量的无用功。考虑到临时对象被删除了,如果编译器将对数据的所有权直接转让给str_copy2不是更好么?意思是不将20000000个字符复制到新地方,再删除原来的字符,而将字符留在原来的地方,而只修改记录。这种方法被称为移动语义。有点悖论的是,移动语义实际上避免了移动原始数据,而是知识修改了记录。

通过定义两个构造函数,一个是常规复制构造函数,它使用const左值引用作为参数,这个引用关联到左值实参,如语句#1中的str;另一个是移动构造函数,它使用右值引用作为参数,改引用关联到右值实参,如语句#2中allcaps(str)的返回值。复制构造函数可执行深复制,而移动构造函数只调整记录。在将所有权转给新对象的过程中,移动构造函数可能修改其实参,这意味着右值引用参数不应是const。

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
#include <iostream>

using namespace std;

class Useless
{
private:
	int n;
	char* pc;
	static int ct;
	void ShowObject() const;
public:
	Useless();
	explicit Useless(int k);
	Useless(int k, char ch);
	Useless(const Useless& f);
	Useless(Useless&& f);
	Useless operator=(const Useless& f);
	Useless operator=(Useless&& f);
	~Useless();
	Useless operator+(const Useless& f) const;
	void ShowData() const;
};

int Useless::ct = 0;
Useless::Useless() {
	++ct;
	n = 0;
	pc = nullptr;
	cout << "default constructor called; number of objects: " << ct << endl;
	ShowObject();
}

Useless::Useless(int k) :n(k) {
	++ct;
	cout << "int constructor called; number of objects:  " << ct << endl;
	pc = new char[n];
	ShowObject();
}

Useless::Useless(int k, char ch) :n(k) {
	++ct;
	cout << "int, char constructor called; number of objects: " << ct << endl;
	pc = new char[n];
	for (int i = 0; i < n; i++) {
		pc[i] = ch;
	}
	ShowObject();
}

Useless::Useless(const Useless& f) :n(f.n) {
	++ct;
	cout << "copy const called; number of objects:	" << ct << endl;
	pc = new char[n];
	for (int i = 0; i < n; i++)
	{
		pc[i] = f.pc[i];
	}
	ShowObject();
}

Useless::Useless(Useless&& f) :n(f.n) {
	++ct;
	cout << "move constructor called; number of objects:	" << ct << endl;
	pc = f.pc;
	f.pc = nullptr;
	f.n = 0;
	ShowObject();
}

Useless Useless::operator=(const Useless& f) {
	cout << "copy assignment operator called: \n";
	if (this == &f) {

		return *this;
	}
	delete[] pc;
	n = f.n;
	pc = new char[n];
	for (int i = 0; i < n; i++)
	{
		pc[i] = f.pc[i];
	}
	return *this;
}

Useless Useless::operator=(Useless&& f) {
	cout << "move assigment operator called: \n";
	if (this == &f) {

		return *this;
	}
	delete[] pc;
	n = f.n;
	pc = f.pc;
	f.n = 0;
	f.pc = nullptr;
	return *this;
}


Useless::~Useless() {
	cout << "destructor called; number of objects:	" << --ct << endl;
	cout << "deleted object:\n";
	ShowObject();
	delete[] pc;
}

Useless Useless::operator+(const Useless& f)const {
	cout << "Entering operate+()\n";
	Useless temp = Useless(n + f.n);
	for (int i = 0; i < n; i++)
	{
		temp.pc[i] = pc[i];
	}
	for (int i = n; i < temp.n; i++)
	{
		temp.pc[i] = f.pc[i - n];
	}
	cout << "temp object:\n";
	cout << "Leaving operatot+()\n";
	return temp;
}

void Useless::ShowObject() const {
	cout << "Number of elements: " << n;
	cout << " Data address: " << (void*)pc << endl;
}

void Useless::ShowData() const {
	if (n == 0)
		cout << "(Object empty)";
	else
		for (int i = 0; i < n; i++)
		{
			cout << pc[i];
		}
	cout << endl;
}

int main() {
	{
		Useless one{ 10,'x' };
		Useless two = one + one; //calls move constructor
		cout << "object one:";
		one.ShowData();
		cout << "object two:";
		two.ShowData();
		Useless three, four;
		cout << "three = one \n";
		three = one; //automatic copy assigment
		cout << "now object three:";
		three.ShowData();
		cout << "and object one =";
		one.ShowData();
		cout << "four = one + two\n";
		four = one + two;
		cout << "now object four:";
		four.ShowData();
		cout << "four = move(one)\n";
		four = move(one);
		cout << "now object four:";
		four.ShowData();
		cout << "and object one =";
		one.ShowData();
	}
}

什么时候调用构造函数和什么时候调用重载的等号,区别在于这个对象是否已经调用过构造函数了。其中move函数是转让拥有权,被转让的那个对象就不拥有对象了。就行线程里面的move一样。

委托构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<string>
using namespace std;
class Notes {
private:
	int k;
	double x;
	string str;
public:
	Notes();
	Notes(int);
	Notes(int, double);
	Notes(int, double, string);
};

Notes::Notes(int kk, double xx, string strr) :k(kk), x(xx), str(strr) {}
Notes::Notes() : Notes(0, 0.11, "Oh") {}
Notes::Notes(int kk) : Notes(0, 0.11, "Oh") {}
Notes::Notes(int kk, double xx) : Notes(0, 0.11, "Oh") {}

例如,上述默认构造函数使用第一个构造函数初始化数据成员并执行其函数体,然后在执行自己的函数体。

继承构造函数

1
2
3
4
5
6
7
8
namespace Box
{
	int fn(int){}
	int fn(double){}
	int fn(const char *){}
}
using namespace::fn;
//这让函数fn的所有重载版本都可以使用。

管理虚方法:override和final

虚方法对实现多态类层次结构很重要,让基类引用或指针能够根据指向的对象类型调用响应的方法,但是虚方法也带来了一些编程陷阱。例如假设基类声明了一个虚方法,而你决定在派生类中提供不同的版本,这将覆盖旧版本,如果特征不匹配,将隐藏而不是覆盖旧版本。

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<iostream>
class Action {
	int a;
public:
	Action(int i = 0) :a(i) {}
	int val()const { return a; }
	virtual void f(char ch)const final { std::cout << val() << ch << "!\n"; }
	virtual void f1(char ch)const { std::cout << val() << ch << "!\n"; }
};

class Bingo :public Action
{
public:
	Bingo(int i = 0) :Action(i) {}
	//virtual void f(char a) const {} //不能重写final修饰的
	virtual void f1(char a) const override {}
	virtual void f(char * ch)const { std::cout << val() << ch << "!\n"; }
};

int main() {
	Bingo b(10);
	char a[2] = "a";
	b.f(a);
	return 0;
}