1.运算符

1.1 单目运算符重载

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
#include<iostream>
using namespace std;
class MyNumber{
private:
int value;
public:
MyNumber(int v):value(v){};
MyNumber& operator++()//重载前置递增运算符++,前置递增运算符返回引用(允许修改原始对象并立即访问修改后的对象)
{
value++;
return *this;
}
MyNumber operator++(int)//重载后置递增运算符++,返回原始对象的副本,int 参数只是为了区分前置和后置递增运算符
{
MyNumber temp(*this);
value++;
return temp;
}
void display()
{
cout<<"value:"<<value<<endl;
}
};
int main()
{
MyNumber num(5);
MyNumber num2=num++;
num2.display();
num.display();
++num;
num.display();
return 0;
}

out:
value:5
value:6
value:7

注:如果只想执行递增运算,可使用++ object,也可使用 object ++,但应选择前者,这样避免创建一个未被使用的临时拷贝。

1.2 转换运算符

转换运算符 const char* :对象的内容转换成 cout 能够接受的类型(const char*)

ostringstream:属于 <sstream> 头文件,并提供了将各种数据类型(如整数、浮点数、字符串等)转换为字符串的能力

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
#include <iostream>
#include <sstream> // 用于 ostringstream
#include <string>
#include<cstdio>
using namespace std;

// 自定义日期类
class Date {
private:
int day, month, year;
string dateInString;

public:
// 构造函数,用于初始化日期对象
Date(int inMonth, int inDay, int inYear) : month(inMonth), day(inDay), year(inYear) {};

// 类型转换运算符,将日期对象转换为 const char* 类型的指针
operator const char*() {
ostringstream formattedDate; // 辅助构建字符串
formattedDate << month << " / " << day << " / " << year;
dateInString = formattedDate.str();
return dateInString.c_str();//dateInString.c_str(),返回一个 const char* 类型的指针
}
};

int main() {
// 创建日期对象,表示圣诞节的日期
Date Holiday(12, 25, 2016);

// 直接输出日期对象,由于重载了类型转换运算符,它将自动转换为字符串并输出
cout << "Holiday is on: " << Holiday << endl;
// 下面是其他使用示例的注释部分
// string strHoliday(Holiday); // 可行!
// strHoliday = Date(11, 11, 2016); // 也是可行的!

return 0;
}

1.3 双目运算符重载

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

class Complex {
private:
double real;
double imaginary;

public:
Complex(double r, double i) : real(r), imaginary(i) {}

// 重载加法运算符 +
Complex operator+(const Complex& other) {
double newReal = real + other.real;
double newImaginary = imaginary + other.imaginary;
return Complex(newReal, newImaginary);
}

// 打印复数的成员函数
void display() {
std::cout << real << " + " << imaginary << "i" << std::endl;
}
};

int main() {
Complex num1(3.0, 4.0);
Complex num2(1.0, 2.0);

Complex sum = num1 + num2; // 使用重载的 + 运算符

std::cout << "Sum: ";
sum.display();

return 0;
}

out:
Sum: 4 + 6i

1.4 重载下标运算符

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
#include <iostream>
#include <stdexcept>
using namespace std;
class MyArray {
private:
int arr[10]; // 假设数组大小为 10

public:
// 重载下标运算符 []
int& operator[](int index) {
if (index < 0 || index >= 10) {
throw std::out_of_range("Index out of range");
}
return arr[index];
}
};

int main() {
MyArray myArray;

// 初始化数组元素
for (int i = 0; i < 10; ++i) {
myArray[i] = i;
}

// 使用下标运算符 [] 访问数组元素并输出
for (int i = 0; i < 10; ++i) {
cout << "myArray[" << i << "] = " << myArray[i] << endl;
}
return 0;
}

out:
myArray[0] = 0
myArray[1] = 1
myArray[2] = 2
myArray[3] = 3
myArray[4] = 4
myArray[5] = 5
myArray[6] = 6
myArray[7] = 7
myArray[8] = 8
myArray[9] = 9

1.5 函数运算符 operator()

operator()让对象像函数,被称为函数运算符。

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>
#include <string>
using namespace std;

// 定义一个类 Display
class Display {
public:
// 重载函数调用运算符 (),用于输出传入的字符串
void operator () (string input) const {//const: 这个关键字表示该运算符函数是一个常量成员函数,告诉编译器在函数内部不会修改对象的状态。
cout << input << endl;
}
};

int main () {
// 创建 Display 类的对象 displayFuncObj
Display displayFuncObj;
// 使用函数调用运算符 () 调用 displayFuncObj,传入字符串参数
displayFuncObj("Display this string! ");
return 0;
}

out:
Display this string!

2.类型转换运算符

2.1 static_cast

static_cast:显式地将一种数据类型转换为另一种数据类型,在执行转换时会进行编译时类型检查,以确保类型转换是合法的。

static_cast的语法如下:

new_type static_cast<new_type>(expression)

其中:

  • new_type 表示要进行的目标类型(目标数据类型)。
  • expression 是要转换的表达式或值。

主要用途:

  • 基本数据类型之间的转换(主要用途):如整数到浮点数,浮点数到整数,以及其他基本数据类型之间的转换。

  • 指针类型之间的转换:在执行时不会进行运行时检查,因此应该谨慎使用

  • 类之间的转换:可以用于父类和子类之间的转换,但它不执行运行时检查,因此应谨慎使用。

  • 枚举类型的转换:可以用于枚举类型之间的转换。

1
2
3
4
5
class Parent { /* ... */ };
class Child : public Parent { /* ... */ };

Parent* parentPtr = new Child();
Child* childPtr = static_cast<Child*>(parentPtr); // 将父类指针转换为子类指针

2.2 dynamic_cast

dynamic_cast:主要用于在继承关系中进行安全的运行时类型识别和类型转换。

dynamic_cast 的语法如下:

dynamic_cast<new_type>(expression)

其中:

  • new_type 表示要进行的目标类型(目标数据类型)。
  • expression 是要转换的指针或引用。

dynamic_cast的主要用途如下:

  • 用于多态类型的安全类型转换dynamic_cast 主要用于处理多态类的情况

*注:dynamic_cast会在运行时检查是否可以进行安全的类型转换。如果转换不合法(例如,试图将基类指针转换为未与之相关的派生类指针),则会返回空指针(对于指针)或引发 std::bad_cast异常(对于引用)。*

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

class Animal {
public:
virtual void speak() {
std::cout << "Animal speaks" << std::endl;
}
};

class Dog : public Animal {
public:
void speak() override {
std::cout << "Dog barks" << std::endl;
}
};

int main() {
Animal* animal = new Dog();

// 使用 dynamic_cast 将基类指针转换为派生类指针
Dog* dog = dynamic_cast<Dog*>(animal);

if (dog) {
// 转换成功,现在可以安全地调用 Dog 类的函数
dog->speak();
} else {
// 转换失败
std::cout << "Failed to cast to Dog" << std::endl;
}

delete animal;

return 0;
}

out:
Dog barks

2.3 const_cast

const_cast:用于在一定情况下去除对象的常量性,可以添加或移除对象的 const 限定符,从而改变对象的常量属性。

const_cast:的语法如下:

const_cast<new_type>(expression)

其中:

  • new_type 表示要进行的目标类型(目标数据类型)。
  • expression 是要转换的指针、引用或对象。

注:

1.const_cast:主要用于去除对象的 const 限定符,使其变为非常量对象,从而允许对其进行修改。

2.const_cast:不会修改对象的实际值,只是修改了对象的类型属性。

主要用途:用于解决某些兼容性问题,例如调用老式库函数,这些函数不将参数标记为常量,但实际上不会修改它们。

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

int main()
{
const int value = 42;
int* nonConstPtr = const_cast<int*>(&value);
*nonConstPtr = 100; // 合法,修改了原本是常量的对象
return 0;
}

3.map和unordered_map(hash_map)的区别

  • 内部实现:

map:基于红黑树(二叉搜索树)实现,元素按照key的顺序进行排序存储(有序,增删改查log(n)的复杂度)

unordered_map:基于哈希表实现,根据键的哈希值分布(增删改查log(1)的复杂度)

注:如果要对元素的key值排序使用map

4.emplace_back和push_back的区别

emplace_back和push_back的用法:向vector末尾添加元素

push_back:

  • vector外构建对象
  • 使用move或拷贝构造添加到vector的末尾

  • 在vector外构建的对象析构了

emplace_back:

  • 直接在vector内构造对象(接受构造该对象所需的参数,并在vector内部直接调用该对象的构造函数)

用法:

1
2
3
std::vector<Person> vec;
vec.push_back(Person("Alice", 30));
vec.emplace_back("Bob", 25);

5.stringstream

头文件:#include <sstream>

作用1:格式化字符串(将各种类型的数据传入字符串中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct SStud
{
int nNumb;
char sName[20];
float fMath;
};
int main()
{
stringstream ostr;
SStud d = { 1008,"学生1",89.5 };
ostr << "学号:" << d.nNumb << "\t" << d.sName << "\t" << d.fMath << endl;
string str = ostr.str();
cout << str << endl;
return 0;
}

作用2:截断字符串(按空格截断)

1
2
3
4
5
string s1, s2, s3;
ostr >> s1 >> s2 >> s3;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;

作用3:以指定字符delim分割字符串

1
2
3
4
5
6
7
8
9
10
11
12
vector<string> split(const string& s, char delim)
{
vector<string> result;
stringstream ss(s);
string item;
while (getline(ss, item, delim))
{
cout << item << endl;
result.emplace_back(item);
}
return result;
}