image.png
image.png

  • cout是带缓冲的,可以进行重定向
  • cerr不带缓冲的,用来打印错误信息
  • clog是带缓冲的

重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
ifstream in ("in. txt");
streambuf * cinbuf = cin. rdbuf ();//save old buf
cin. rdbuf ( in. rdbuf ());//redirect cin to in. txt !
ofstream out (" out. txt ");
streambuf * coutbuf = cout. rdbuf (); //save old buf
cout. rdbuf ( out. rdbuf ()); //redirect cout to out. txt !
string word;
cin >> word; //input from the file in. txt
cout << word << " ";//output to the file out. txt
cin. rdbuf ( cinbuf );//reset to standard input again
cout. rdbuf ( coutbuf ); //reset to standard output again
cin >> word; //input from the standard input
cout << word; //output to the standard input

I/O处理

对输出操作符<<进行重载,只能采用友元函数的形式进行,而不能将operator<<()申明为ostream类的成员函数。这是因为ostream是在C++标准中定义的类,不允许用户随便修改。所以,要将类someClass的对象输出到标准输出对象,只能采用将operator<<()重载为全局函数,申明为someClass类的友元的形式进行。

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
class CPoint2D{
double x, y;
public:
friend ostream& operator << (ostream&, CPoint2D &);
};
//全局函数:传入一个ostream对象和一个自定义对象
ostream& operator << (ostream& out, CPoint2D& a){//引用类型保证能连续输出
out << a.x << "," << a.y << endl;
return out;
}
CPoint2D a;
cout << a;
---------------------------------------------------------------------------
class CPoint3D: public CPoint2D
{ double z;

}
CPoint3D b;
cout << b;//只显示b.x和b.y,而没显示b.z
class CPoint3D: public CPoint2D
{ double z;
friend ostream& operator << (ostream &, CPoint3D &);
}
ostream& operator << (ostream& out, CPoint3D & b){
out << b.x << "," << b.y <<"," << b.z << endl;
return out;
}
//问题:3D对象被2D指针指向,cout调用了2D的版本,解决:虚化

虚化:虚化非成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class CPoint2D
{ double x, y;
public:

virtual void display(ostream& out)
{ out << x << ‘,’ << y << endl; }
};
// 针对基类进行操作符重载
ostream& operator << (ostream& out, CPoint2D &a)
{
a.display(out); // 调用方法,虚函数,调用实际类型
return out;
// 和虚函数的作用一样 -- 让一个全局函数针对不同派生类型,进行不同处理,定义一个
// 虚函数,直接调用对象的虚函数,全局函数是一个非虚接口
}

class CPoint3D: public CPoint2D
{ double z;
public:

void display(ostream& out)
{ CPoint2D::display(); out << ‘,’<< z << endl; }
};

虚拟化构造器

image.png
image.png

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
class NLComponent {…};
class TextBlock :public NLComponent {…};
class Graphic :public NLComponent {…};
class NewsLetter{
public:
NewsLetter(istream& str){
while (str)
components.push_back(readComponent(str));
}
static NLComponent * readComponent(istream& str);
NewsLetter(const NewsLetter& rhs){//拷贝构造函数
for (list<NLComponent *>::iterator it=rhs.component.begin();it != rhs.component.end(); ++it )
//期望有一个虚函数可以拷贝自己,不希望进行的是浅拷贝
component.push_back();//new TextBlock? Graphic?
}
private:
list<NLComponent *> components;
}
//虚化构造器,因为构造函数不能是虚函数
virtual NLComponent *clone() const = 0; // 纯虚函数,代表一个接口
//原型模式:添加clone,使得构造也具有多态的行为
virtual TextBlock *clone() const{ // 构造器本身是一个成员函数,不需要全局
return new TextBlock(*this);
}
virtual Graphic *clone() const{
return new Graphic (*this);
}
NewsLetter::NewsLetter( const NewsLetter& rhs){
for ( list<NLComponent *>::iterator it=rhs.component.begin();
it != rhs.component.end(); ++it )
component.push_back((*it)->clone());
}
//typeid(*it)==typeid(TextBlock)判断对象的类型,必须带有虚函数。
// 如果不带有虚函数,只会得到参数的类型。所以可以创建一个虚的构造器RTTI

//Question
class BST {};
class BalancedBST: public BST {};
void printBSTArray(ostream& s, const BST array[], int numElements){
for (int i=0; i < numElements; i++)
s << array[i];
}
BalancedBST bBSTArray[10];
printBSTArray(cout, bBSTArray, 10);
//问题是?array[i]是指针算法的缩写,数组每次偏移地址是sizeof(BST),而不是sizeof(BalancedBST),会出现问题。

C11新特性

右值引用 R-value Reference

image.png
image.png
image.png

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
class MyArray {
int size;
int *arr;
public:
MyArray():size(0),arr(NULL){}
MyArray(int sz):
size(sz),arr(new int[sz]) {
//init array here…
}
MyArray(const MyArray &other):
size(other.size),
arr(new int[other.size]) {
for (int i = 0; i < size; i++) {
arr[i] = other.arr[i];
}
}
//main函数中第三种声明方式的移动构造
MyArray (MyArray &&other):
size(other.size), arr(other.arr) {
other.arr = NULL;
}
~ MyArray() {
delete[] arr;
}
}
MyArray change_aw(const MyArray &other)
{
MyArray aw(other.get_size());
//Do some change to aw.
//….
return aw;
}

int main() {
MyArray myArr(5);
MyArray myArr2 = change_aw(myArr);//调用了两次拷贝,先将myArr传入的时候进行一次拷贝,返回之后再次进行拷贝,比较大的开销
MyArray &&myArr2 = change_aw(myArr);//右值函数,直接用移动构造函数,右值引用造成的维护困难
MyArray myArr2 = change_aw(myArr);//有了新的移动构造函数,自动适配,15min,提高拷贝速度在C++中使用移动构造函数
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyArray {
public:
//…
MyArray &operator=(const
MyArray &other) {
if (this == &other)
return *this;
if (arr) { delete[] arr; arr = NULL;
}
size = other.size;
memcpy(arr, other.arr, size * sizeof(int));
return *this;
}
MyArray &operator=(ArrayWrapper &&other) { // 匹配移动赋值操作符的重载
size = other.size;
arr = other.arr;
other.arr = NULL;
return *this; // 赋值操作符重载
}
}
int main() {
MyArray myArr;
myArr = MyArr(5);//MyArr是临时对象,无法直接进行使用,可以直接移动过去。19min的内容
}

拷贝构造、拷贝赋值、移动构造、移动赋值、析构函数
重定义其中一个,另外四个都不再默认生成

外部模板 Extern Templates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//myfunc.h
template<typename T>
void myfunc(T t){}

//test.cpp
#include "myfunc.h"
int foo(int a){
myfunc(1); // 在链接的过程中,会进行实例化
return 1;
}
//main.cpp
#include "myfunc.h"
//如果没有以下的模板,那么编译器会先去实例化模板,新的方式外部模板可以避免多次实例化的问题
/*Tell compiler: this instance has been
instantiated in another module!*/
extern template void myfunc<int>(int); // 已经被实例化了且会被链接

int main() {
myfunc(1); // 就不会重复实例化了
}

常量表达式 Constant Expression

  1. 提供了更一般的常量表达式

  2. 允许常量表达式使用用户自定义类型

  3. 提供一种方法来确保在编译时完成初始化

  4. 必须在编译的时候可以确定常量表达式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    enum Flags { GOOD=0, FAIL=1, BAD=2, EOF=3 };
    constexpr int operator| (Flags f1, Flags f2) {
    return Flags(int(f1)|int(f2));
    }//如果不加constexpr则结果被认为是变量不能使用在case中
    void f(Flags x) {
    switch (x) {
    case BAD: /* ... */break;
    case EOF: /* ... */ break;
    case BAD|EOF: /* ... */ break;//OK,必须是简单的确认的值
    default: /* ... */ break;
    }
    }
    void f(Flags x) {
    switch (x) {
    case bad_c(): /* ... */break;
    case eof_c(): /* ... */ break;
    case be_c(): /* ... */ break;
    default: /* ... */ break;
    }
    }
    constexpr int bad_c(); // 只要函数的返回值永远是一个常量
    constexpr int eof_c();
    constexpr int be_c();
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    struct Point {
    int x,y;
    constexpr Point(int xx, int yy) : x(xx), y(yy) { }
    };
    int main() {
    // const对象 -- 对象是不能通过接口修改的
    // constexpr -- 对象的值是可以在编译时确定的,同时也是一个const
    constexpr Point origo(0,0);//完全常量,在常量表上
    constexpr int z = origo.x;
    // 相当于对象都是一组值,不需要重复开辟空间
    constexpr Point a[] = {Point(0,0), Point(1,1), Point(2,2) };
    constexpr int x = a[1].x; // x becomes 1
    }
  5. 所有评估都可以在编译时完成,所有对象都可以看做一组值。 因此,提高了运行时间效率。

  6. 编译时确定的

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
bool cmpInt(int a, int b) {return a < b;}
class CmpInt {
bool operator()(const int a, const int b) const {
return a < b;
} // 函数调用操作符重载
};
int main() {
std::vector<int> items { 4, 3, 1, 2 };
std::sort(items.begin(), items.end(), cmpInt); //Function Pointer 函数指针
std::sort(items.begin(), items.end(), CmpInt()); //Function Object (Functor) 函数对象
std::sort(items.begin(), items.end(), // [] 代表 lambda函数
[](int a, int b) { return a < b; } //Lambda Function
); // 实例化成std::function
return 0;
}
template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp) {
//…
if ( comp(*it1, *it2) )
//…
}
//std::function 是C++对所有可调用的函数的封装:使用函数、函数对象、lambda函数
std::function<bool(int, int)> f1(cmpInt);
std::function<bool(int, int)> f2(CmpInt);
std::function<bool(int, int)> f3([](int a, int b) { return a < b;} );
// 1.不需要特别实例化 2.lambda表达式可以统一实例化为std::function对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vector<string> str_filter(vector<string> &vec, function<bool(string &)> matched){
vector<string> result;
for (string tmp : vec) {
if (matched(tmp))
result.push_back(tmp);
}
return result;
}
//可以会用局部变量,40min
int main(){
vector<string> vec = {"www.baidu.com", "www.kernel.org", "www.google.com"};
string pattern = ".com";
vector<string> filterd = str_filter(vec,
[&](string &str) { // [&]:以引用的方式,捕获有效变量,不需要传入新的参数
if (str.find(pattern) != string::npos)
return true;
return false;
});
}
符号 含义
[] Capture nothing
[&] Capture any referenced variable by reference
[=] Capture any referenced variable by making a copy 以值拷贝的方式传入,不会产生副作用
[=, &foo] Capture any referenced variable by making a copy, but capture variable foo by reference
[bar] Capture bar by making a copy; don’t copy anything else

Delegating Constructor 委托构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define MAX 256
class X {
int a;
void validate(int x) { if (0<x && x<=MAX) a=x; else throw bad_X(x); }
public:
X(int x) { validate(x); }
X() { validate(42); }
// ...
};
class X {
int a;
public:
X(int x) { if (0<x && x<=max) a=x; else throw bad_X(x); }
X() :X(42) { } // 在一个构造函数中,委托另外一个构造函数。默认构造函数可以委托另外一个构造函数
// ...
};
X(int x = 42) ? // 默认参数是有参数的,和无参默认构造函数是不同的

Uniform Initialization 统一初始化

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
//Old style initialization
vector<int> vec;
vec.push_back(1);
//…
//New style initialization
vector<int> vec = {1, 2, 3};
//Compiler will translate {} as initializer_list<int> 新的初始化表
template class vector<T> {
//..
vector(initializer_list<T> list) {
for (auto it = list.begin(); it != list.end(); ++it)
push_back(*it);
}
};
int arr[] = {1, 2, 3}; //OK
vector<int> vec = {1, 2, 3};
A a= {1, 2, 3};

class A{
int x, y, z;
//Default generated by compiler
A(initializer_list<int> list) { // 接受初始化列表参数
auto it = list.begin();
x = *it++;
y = *it++;
z = *it;
}
};
//Uniform Initialization achieved!
int arr[] = {1, 2, 3};
vector<int> vec = {1, 2, 3};
A a = {1, 2, 3}; // 都可以通过初始化列表的方式进行初始化

nullptr 空指针

1
2
3
4
5
6
7
8
void f(int);//f(0) 两者都可以解释
void f(char*);

// 新版本
f(0); // call f(int)
f(nullptr); // call f(char*)
// 原来的NULL就是0
f(NULL);// call f(int)