简介

一些闲来无事写的小代码,没有独立写文的必要,就都放在这里了


重载 to_string

重载to_string使得map,vector等容器可以转化为string字符串

使用 ostringstream 进行链式字符串拼接


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
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <map>

using namespace std;

string to_string(string str) {
return str;
}

template<typename K, typename V>
string to_string(const pair<K, V>& p) {
ostringstream o;
o << to_string(p.first) << ":" << to_string(p.second);
return o.str();
}

template<typename T>
string to_string(const T& begin, const T& end) {
ostringstream o;
for (T it = begin; it != end; ++it) {
if (it != begin)
o << ", ";
o << to_string(*it);
}
return o.str();
}

template<typename K, typename V>
string to_string(const map<K, V>& m) {
ostringstream o;
o << "{ " << to_string(m.begin(), m.end()) << " }";
return o.str();
}

template<typename T>
string to_string(const vector<T>& v) {
ostringstream o;
o << "[ " << to_string(v.begin(), v.end()) << " ]";
return o.str();
}

int main() {
map<string, string> m;
m.insert(pair<string, string>("name", "test"));
m.insert(pair<string, string>("age", "22"));
m.insert(pair<string, string>("sex", "男"));
vector<int> v = { 1,2,3,4,5 };
cout << to_string(m) << endl;
cout << to_string(v) << endl;
return 0;
}


cout 输出true, false

使cout输出true,false而不是1,0

在默认情况下,cout会将true, false转换为1, 0输出,而不是输出true, false

解决: 使用boolalpha,头文件iostream

当使用boolalpha后,以后的bool类型结果都将以true或false形式输出,除非使用 noboolalpha取消 boolalpha流的格式标志


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>

using namespace std;

int main() {
cout << "boolalpha" << endl;
cout << boolalpha;
cout << "true : " << true << endl;
cout << "false : " << false << endl << endl;
cout << "noboolalpha" << endl;
cout << noboolalpha;
cout << "true : " << true << endl;
cout << "false : " << false << endl;
return 0;
}


函数指针

函数指针的使用

函数指针的定义:函数返回值类型 (* 指针变量名) (函数参数列表);


1
2
3
void func(int num){}
void (*p)(int);//定义了一个返回值为void,参数列表为int,且只有一个参数的函数指针
p = func;//指向函数

函数指针的使用


1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>

using namespace std;

void func(int num) {
cout << num << endl;
}

int main() {
void(*p)(int) = func;
p(1);//理解:函数名就是指针,所以使用函数指针与使用函数一样
(*p)(1);//理解:指针p指向函数,所以要解析指针
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
#include <iostream>

using namespace std;

class Base {
public:
Base() {
cout << "make Base" << endl;
}

~Base() {
cout << "del Base" << endl;
}
};
class Son : public Base {
public:
Son() {
cout << "make Son" << endl;
}

~Son() {
cout << "del Son" << endl;
}
};

int main() {
Base* p = new Son();
delete p;
}

构造01

根据输出可见,Son的析构函数未被调用,存在内存泄露问题,将Base类的析构函数声明为虚函数即可解决

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

using namespace std;

class Base {
public:
Base() {
cout << "make Base" << endl;
}

virtual ~Base() {
cout << "del Base" << endl;
}
};
class Son : public Base {
public:
Son() {
cout << "make Son" << endl;
}

~Son() {
cout << "del Son" << endl;
}
};

int main() {
Base* p = new Son();
delete p;
}

构造01


成员变量初始化顺序

成员变量初始化顺序

成员变量的声明顺序,决定了成员变量的初始化顺序。假设 Date 类中的构造函数为:

1
2
public:
Date() : y(2016), m(2), d(4) {}

此时,成员变量,在类中的声明顺序 = 构造函数初始化列表顺序,故 y, m, d 都能被顺利的 初始化为对应的值。

而当成员变量,在类中的声明顺序 ≠ 构造函数初始化列表顺序 时,如下:

1
2
public:
Date() : y(2016), d(4), m(d-2) {}

根据成员变量的声明顺序,y 首先被初始化为 2016,然后再初始化 m,但由于 d 并未被初始 化,所以 m 的值是随机的,最后初始化 d 为 4

因为,类的成员变量在初始化时,其初始化的顺序只与声明顺序有关,而与初始化顺序无关。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

using namespace std;

class Date
{
public:
Date() :y(2021), d(4), m(d - 2) {};

void printDate() {
cout << "y : " << y << endl;
cout << "m : " << m << endl;
cout << "d : " << d << endl;
}
private:
int y, m, d;
};

int main() {
Date date;
date.printDate();
return 0;
}

初始化


C++的函数重载

C++的函数重载
  • 为什么要进行函数重载

    • 为了简化编程
    • 提高编程效率
    • 增加代码可读性
  • 函数重载的规则

    • 函数名相同
    • 函数的参数列表不同(参数个数,类型,顺序不同等)
    • 函数的返回值可以不同
    • 仅仅返回类型不同不足以成为函数的重载
  • 命名倾轧

    • C++函数重载底层实现原理是C++利用name mangling(倾轧)技术,来改名函数名,区分参数不同的同名函数。
    • 编译器通过函数名和其参数类型识别重载函数。为了保证类型安全的连接(typesafe linkage),编译器用参数个数和参数类型对每一个函数标识符进行专门编码,这个过程有时称为“名字改编”(name mangling)或“名字修饰”(name decoration)。类型安全的连接使得程序能够调用合适的重载函数并保证了参数传递的一致性。编译器能够检测到并报告连接错误。
  • 查看符号表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include<iostream>

    int func(int a) {
    return a;
    }
    double func(double a) {
    return a;
    }

    int main() {
    return 0;
    }
    • 使用 g++ -c 只编译不链接,生成目标文件

    • 使用 nm 查看目标文件(.o)

      重载