0%

2020年5月18日 下午4:45

  1. 我再原先的版本上增加了:
    1. 默认初始化
    2. 字符串的输入的初始化
    3. <<的重载
    4. 参考:C++ 高精度整数,大数的进制转换_c/c++_chenmeiqi的博客-CSDN博客
  2. 大数10进制的原理
    1. 本质:将一个超大10进制数进行分解
      1. ….
      2. 多少个10^8
      3. 多少个10^4
      4. 多少个10^0
    2. bigInteger数据结构:
  3. 其中的bug局限:
    1. 进行乘、除、余数时,只能使用0~10^4次范围内的10进制数,因为这份代码每次进位只能进一位,也就是10^4
      1. 解决办法:digit[]中的每个元素的最大值,可以从9999 -> MAX_INT
      2. 这样我们就可以乘以MAX_INT
    2. bigInteger不支持:
      1. bigInteger * bigInteger
      2. bigInteger / bigInteger
      3. bigInteger % bigInteger
    3. bigInteger支持:
      1. bigInteger * int
      2. bigInteger / int
      3. bigInteger % int
  4. 代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    int main() {
    bigInteger A("999999999999999999");
    bigInteger B = A * 10;
    bigInteger C = B / 10;
    int D = C % 100;
    cout << A << endl;
    cout << B << endl;
    cout << C << endl;
    cout << D << endl;

    cout << 1 << endl;
    return 0;
    }

    输出:
    999999999999999999
    9999999999999999990
    999999999999999999
    99
    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
    #include <iostream>
    #include <cmath>
    using namespace std;
    unsigned long max_len;
    struct bigInteger{ // 大数结构
    int digit[1000] = {0};
    int size;
    // 默认初始化
    bigInteger(){

    }
    // 使用字符串初始化
    bigInteger(string input) {
    size=0;
    int s_len=input.length();
    int len=0;
    if(s_len%4!=0){ // 一个存 4 位,不足的+1
    len=s_len/4+1;
    }
    else{
    len=s_len/4;
    }
    for(int i=0;i<len;i++){
    int pos = max(0,s_len-1-4*i-3);
    for (int j=s_len-1-4*i; j>=pos; j-=1) { // 从字符串后往前存
    digit[i]+=(input[j]-'0')*pow(10,s_len-4*i-j-1); // char to int, 每次存4位,所以 -4*i
    }
    size=i+1;
    }
    }
    // 重载输出
    friend ostream& operator<<(ostream& os, bigInteger &x)
    {
    os << x.out() << endl;
    return os;
    }
    // 10进制的字符串形式
    string out(){
    unsigned long temp;
    string res="";
    string s;
    for (int i=size-1; i>=0; i--) {
    s=to_string(digit[i]); // int 转 string
    temp=res.length(); // 结果字符串长度
    if(i!=size-1){ // 最高位不补 0
    for (unsigned long len=s.length(); len<4; len++) { // 其余位不够 4 位补 0
    if(temp+1==max_len){ // 结果字符串长度将等于最大位数,不再补 0(针对最低位)
    break;
    }
    else{ // 补 0
    s="0"+s;
    temp++;
    }
    }
    }
    res+=s; // 结果字符串拼接
    }
    return res;
    };


    bigInteger operator + (const bigInteger & B) const{
    bigInteger res;
    bool ifCarry=false; // 是否进位
    int carry=0; // 进位值
    int index=0; // 在第几位进位
    res.size =max(size, B.size);
    for (int i=0; i<res.size; i++) { // 逐个相加
    res.digit[i]=B.digit[i]+digit[i]+carry;
    if(res.digit[i]>9999){
    index=i;
    ifCarry=true;
    }
    carry=res.digit[i]/10000;
    res.digit[i]=res.digit[i]%10000;
    }
    if(ifCarry && index==res.size-1){ // 有进位且进位后大于原来位数
    res.digit[res.size]=1;
    res.size++;
    }
    if(ifCarry && index==res.size-2){ // 有进位但位数不变
    max_len++;
    }
    return res;
    };


    bigInteger operator * (int x) const{
    bigInteger res;
    bool ifCarry=false; // 是否进位
    int carry=0; // 进位
    int index=0; // 在第几位进位
    res.size =size;
    for (int i=0; i<res.size; i++) { // 逐个相乘
    res.digit[i]=x*digit[i]+carry;
    if(res.digit[i]>9999){
    index=i;
    ifCarry=true;
    }
    carry=res.digit[i]/10000;
    res.digit[i]=res.digit[i]%10000;
    }
    if(ifCarry && index==res.size-1){ // 有进位且进位后大于原来位数
    res.digit[res.size]=carry;
    res.size++;
    }
    return res;
    };


    bigInteger operator / (int x)const{
    bigInteger res;
    int reminder=0; // 余数
    res.size =size;
    for (int i=res.size-1; i>=0; i--) { // 逐个相除
    res.digit[i]=(digit[i]+reminder*10000)/x;
    reminder=(digit[i]+reminder*10000)%x;
    }
    if(res.size==1 && digit[0]<x){ // 被除数小于 x
    res.digit[0]=0;
    }
    if(res.size>1 && digit[res.size-1]<x){ // 最高位有余数,位数 -1
    res.digit[res.size-1]=0;
    res.size--;
    }
    return res;
    }

    int operator % (int x)const{
    bigInteger res;
    int reminder=0; // 余数
    res.size =size;
    for (int i=res.size-1; i>=0; i--) { // 逐个相除
    res.digit[i]=(digit[i]+reminder*10000)/x;
    reminder=(digit[i]+reminder*10000)%x;
    }
    return reminder;
    }


    };

2020年5月17日 上午12:12

总结:

  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
    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
    // Demo9-11.cpp : 定义控制台应用程序的入口点。
    #include "stdafx.h"

    #include <iostream>
    #include <thread>
    #include <mutex>
    using namespace std;


    // 存钱
    void Deposit(mutex& m, int& money)
    {
    // 锁的粒度尽可能的最小化
    for(int index = 0; index < 100; index++)
    {
    m.lock();
    money += 1;
    m.unlock();
    }
    }
    // 取钱
    void Withdraw(mutex& m, int& money)
    {
    // 锁的粒度尽可能的最小化
    for (int index = 0; index < 100; index++)
    {
    m.lock();
    money -= 2;
    m.unlock();
    }
    }

    int main()
    {
    // 银行存取款
    //int money = 2000;
    //mutex m;
    //cout << "Current money is: " << money << endl;
    //thread t1(Deposit, ref(m), ref(money));
    //thread t2(Withdraw, ref(m), ref(money));
    //t1.join();
    //t2.join();
    //cout << "Finally money is: " << money << endl;

    //线程交换
    //thread tW1([]()
    //{
    // cout << "ThreadSwap1 " << endl;
    //});
    //thread tW2([]()
    //{
    // cout << "ThreadSwap2 " << endl;
    //});
    //cout << "ThreadSwap1' id is " << tW1.get_id() << endl;
    //cout << "ThreadSwap2' id is " << tW2.get_id() << endl;

    //cout << "Swap after:" << endl;
    //swap(tW1, tW2);
    //cout << "ThreadSwap1' id is " << tW1.get_id() << endl;
    //cout << "ThreadSwap2' id is " << tW2.get_id() << endl;
    //tW1.join();
    //tW2.join();

    //// 线程移动
    thread tM1( []() { ; } );
    //tM1.join();
    cout << "ThreadMove1' id is " << tM1.get_id() << endl;
    cout << "Move after:" << endl;
    thread tM2 = move(tM1);
    cout << "ThreadMove2' id is " << tM2.get_id() << endl;
    cout << "ThreadMove1' id is " << tM1.get_id() << endl;
    tM2.join();

    return 0;
    }

2020年5月17日 上午12:09

总结:

  1. 这个里面涉及到几个知识点:
    1. std::bind1st, std::bind2nd - cppreference.com
      1. 绑定给定参数 x 到给定二元函数对象 f 的第一或第二参变量。即,在产生的包装器内存储 x ,若调用它,则将 x 传递为 f 的第一或第二参数。
    2. std::transform - cppreference.com
      1. std::transform 应用给定的函数到范围并存储结果于始于 d_first 的另一范围。
  2. transform,bind1st,bind2nd,count_if等这些方法的实现机制关键是使用了函数指针,作为传入的参数或者函数的返回

例子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
39
40
41
// Demo9-4.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"

#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
#include<iostream>
using namespace std;



int main()
{
// transform和lambda表达式
int ones[] = { 1, 2, 3, 4, 5 };
int twos[] = { 10, 20, 30, 40, 50 };
int results[5];
transform(ones, ones + 5, twos, results, std::plus<int>()); // 数组元素依次相加并返回
for_each(results, results + 5,
[ ](int a)->void {
cout << a << endl; } ); // lambda表达式(匿名函数)
cout << endl;

// find
int arr[] = { 0, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8 };
int len = sizeof(arr) / sizeof(arr[0]);
vector<int> iA(arr + 2, arr + 6); // {2,3,3,4}
//vector<int> iA;
//iA.push_back(1);
//iA.push_back(9); // {1, 9}
cout << count(arr, arr + len, 6) << endl; // 统计6的个数
cout << count_if(arr, arr + len, bind2nd(less<int>(), 7) ) << endl; // 统计<7的个数
cout << binary_search(arr, arr + len, 9) << endl; // 9找不到
cout << *search(arr, arr + len, iA.begin(), iA.end()) << endl; // 查找子序列

cout << endl;

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
// Demo9-5.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"


#include <algorithm>
#include <iostream>
using namespace std;

// 输入一个不存在重复字符的字符串,打印出字符串中字符的全排列。
//比如:
//输入: "123" 3*2*1 = 3!
//输出: 123
// 132
// 213
// 231
// 321
// 312
//f(123) = 1+f(23), f(23) = 2+f(3), f(3) = 3 递归
void swap(char* a, char* b)
{
char temp = *a;
*a = *b;
*b = temp;
}
void Permutation(char* pStr, char* pPostion)
{
// 基准点
if (*pPostion == '\0')
{
cout << pStr << endl;
}
else
{
for (char* pChar = pPostion; *pChar != '\0'; pChar++)
{
// 依次和后面的字符交换
swap(*pChar, *pPostion);

Permutation(pStr, pPostion + 1);

// 换回来
swap(*pChar, *pPostion);
}
}
}


int main()
{
char test[] = "132";
Permutation(test, test);
cout << endl;

// 用STL输出全排列
// 注意:必须要保证数组顺序,
do
{
cout << test[0] << test[1] << test[2] << endl;
} while (next_permutation(test, test + 3));
cout << endl;

char test2[] = "321";
// 注意:必须要保证数组顺序,
do
{
cout << test2[0] << test2[1] << test2[2] << endl;
} while (prev_permutation(test2, test2 + 3));

return 0;
}

2020年5月17日 下午11:43
知无涯之C++ typename的起源与用法

总结:

  1. typename解决的问题是:
    1. 避免在类作用域::操作中T::iterator,将原本应该使用嵌套类型的,使用成了静态数据成员 or 静态成员函数。
    2. 使用typename标识T::iterator之后 typename T::iterator,这样编译器就会帮我们检查一下是不是使用嵌套类型的,多了一步检查,这样就安全多了
  2. typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
    1. __type_traits## 这个模板类中的has_trivial_destructor## 嵌套类型定义一个叫做trivial_destructor## 的别名,清晰明了

2020年5月17日 下午9:21

我理解的STL的宏观设计:

  1. 层次话的结构图:在<STL源码剖析>中作者是扁平化的图P6
  2. 要上升一个层次,从编程范式的本质角度去理解STL的设计、各个组件之间的逻辑关系
    1. 首先,需要理解编程范式的本质是做到control、logic、data的分离
      1. 理解control、logic、data、泛型、函数式编程
      2. 编程范式理解
    2. 站在这个角度:
      1. algorithm ==> control
      2. Functor == > logic
      3. Container == > data
    3. iterator 是Container 的一部分,也就是说iterator == data:
      1. Container 提供 iterator,因为Container最清楚iterator的操作,所以在编程实现的时候,iterator是作为Container的内部类来实现的
    4. 总结:
      1. STL可以说是编程范式在C++这门语言的最佳实践,它完美的达到了编程范式所追求的control、logic、data的分离。
      2. 当我们需要设计一个control、logic、data的分离的项目功能的时候,就需要把STL作为参考,尤其时候在C++下,STL就告诉了我们应该如何利用C++语言的特性 + 合适的设计,来达到control、logic、data的分离分离
    5. 这部分在使用STL的时候,也可以看出来:
      1. 里有用的函数

为什么需要iterator_traits,没有iterator_traits的五种特性会怎样

  1. 我们知道五种特性,其实都是为了说明当前容器对应的类型
    1. 前四种类型:元素类型、距离类型、指针类型与reference类型
      1. 如果iterator不提供类型这前四种,那么iterator的上层algorithm,就无法做到泛型。
      2. 证明在 模板编程:对循环for分步骤STL如何实现泛型
    2. 而第五种类型:迭代器类型
      1. 为了algorithm层次可以根据迭代器类型做重载函数的设计,来区别五种不同的迭代器(P92-<STL源码剖析>)
  2. 核心本质:没有iterator_traits,做不到泛型!上层无法获得下层数据对象的类型,因此,下层应该给上层留出接口,能够上上层自己获取。
  3. 在Traits中可以引申出一个重要的语法点typename:
    1. [摘抄]C++ typename的起源与用法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      //!!! 自定义迭代器必须提供iterator_traits的五种特性,分别是迭代器类型、元素类型、距离类型、指针类型与reference类型!!!
      //template<class Iterator>
      //struct iterator_traits
      //{
      // typedef typename Iterator::difference_type difference_type;
      // typedef typename Iterator::value_type value_type;
      // typedef typename Iterator::pointer pointer;
      // typedef typename Iterator::reference reference;
      // typedef typename Iterator::iterator_category iterator_category;
      //};

2020年5月17日 下午5:46

总结:

  1. 有些统计类型的题目,key->value是一对多的,这时需要使用multimap,关于multimap我总结了三种可能会用到的遍历方式:
    1. 对所有元素的遍历,和map效果一样
    2. 对包含指定key的元素遍历
    3. 结合上面的1和2,对multimap中的所有元素按key进行遍历
  2. 这里关键灵活使用multimap的成员函数:
    1. equal_range
      1. 返回容器中所有拥有给定关键的元素范围。范围以二个迭代器定义,一个指向首个不小于 key 的元素,另一个指向首个大于 key 的元素。首个迭代器可以换用 lower_bound() 获得,而第二迭代器可换用 upper_bound() 获得。
    2. lower_bound()
    3. upper_bound()
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

int main()
{
std::multimap<int, char> dict {
{1, 'A'},
{2, 'B'},
{2, 'C'},
{2, 'D'},
{4, 'E'},
{3, 'F'}
};
// 1.每个元素遍历输出
cout << "第一种:每个元素遍历输出" << endl;
for(auto pair : dict) {
cout << pair.first << " " << pair.second << endl;
}

// 2.指定key,进行遍历
cout << "第二种:指定key,进行遍历" << endl;
auto range = dict.equal_range(2); // range的类型是一个pair
for (auto i = range.first; i != range.second; ++i)
{
std::cout << i->first << ": " << i->second << '\n';
}

// 3.对multimap中的所有元素按key进行遍历
cout << "第三种:对multimap中的所有元素按key进行遍历" << endl;
for(auto it = dict.begin(); it != dict.end(); it = dict.upper_bound(it->first))
{
auto range = dict.equal_range(it->first);
cout << it->first << ":" << endl;
while(range.first != range.second)
{
cout << " " << range.first->second << endl;
++range.first;
}
}
}

输出:
第一种:每个元素遍历输出
1 A
2 B
2 C
2 D
3 F
4 E
第二种:指定key,进行遍历
2: B
2: C
2: D
第三种:对multimap中的所有元素按key进行遍历
1:
A
2:
B
C
D
3:
F
4:
E

参考:

  1. std::multimap 按照key遍历— - 你好阿汤哥 - 博客园
  2. std::multimap<Key,T,Compare,Allocator>::equal_range - cppreference.com

2020年5月17日 下午4:16

总结:

  1. 推导时间复杂度的思路:
    1. huahua的思路是:
      1. 先写出代码,然后根据代码的形式来总结出时间复杂度的递推表达式
    2. 我的另一种思路:
      1. 从计算机外行的角度来思考,别用编程角度来解决,从数学的角度
      2. 先说说用人话如何解决这个问题,然后将问题用数学定义变量、函数的角度进行抽象的描述
      3. 这样自然就得到了时间复杂度
  2. 当具体遇到扩展的问题的时候:
    1. 我们就没必要按1所说的纠结我们常用的时间复杂度是如何计算出来的,在扩展的时候我们就把常用的递归问题时间复杂度当做已知,不管你是背会还是理解,千万别从头开始推理
    2. 这样通过类比的方法,我们就可以大大利用储备的知识来计算新问题的时间复杂度

主要参考:

花花酱 Time/Space Complexity of Recursion Functions SP4 – Huahua’s Tech Road



2020年5月17日 下午3:49
花花酱 Time/Space Complexity of Recursion Functions SP4 – Huahua’s Tech Road

总结:

  1. 排列和组合的思维本质:递归。递归代表着:递推表达式
    1. 排列和组合总是放在一起讲,我觉得其实这就是两个完全不相关的问题
    2. 在combination中,T(n) = T(n-1) + T(n-2) + T(n-3) + …. + T(1),这个式子的语义是:我们将一个n个元素可以有多少种组合的问题T(n),拆解为(n-1) n(n-2)…n(1)的子问题和。
      1. Eg:[1,2,3] 我们就拆解成:
        1. 当使用3个元素时,有少种组合数
        2. 当使用2个元素时,有多少种组合数
        3. 当使用1个元素时,有多少种组合数
    3. 在permutation中,我们将问题拆解成T(n) = n * T(n-1)
      1. 语义:在一个排列的第一个位置上,有n种放置元素方法,在每种放置下,后面还有T(n-1)种排列方式
      2. 我们将其加起来,就变成了T(n) = n * T(n-1)
    4. 解决排列和组合问题的思路对比:
      1. 区别:
        1. 组合问题的解决更像是对问题本身的分类
        2. 排列问题的解决更像是一步步的过程模拟
      2. 相同:
        1. 不论是组合的分类,还是排列的模拟过程,在编程中都可以通过递归这个编程技巧来作为实现方式
        2. 因为,都涉及相同的子问题。
  2. 理解了排列和组合的本质是递归以外,我们在真正解决问的时候还依靠于一个技巧:递归树。递归树的优势是形象,我们再写代码的时候,如果脑子里是按照递归树来写,就很容易分析边界等条件,所以说递归树这点要比直接的使用递归表达式来写更好,递归表达式更适合问题是一个问:有多少种组合的结果,这样直接数字型的答案:
    1. 递归树的优点:
      1. 很容易分析边界等条件
      2. 在遇到题目扩展的时候,更容易进行扩展
    2. 递推表达式的优点:
      1. 在计算复杂度的时候更加方便

2020年5月16日 下午11:50

总结:

  1. 对于algorithm中的两个函数:
    1. sort
    2. for_each
  2. 有两种思路:
    1. 函数 -> 函数模板
    2. struct仿函数 -> struct仿函数模板
  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
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
// Demo9-3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <algorithm>
#include <iostream>
using namespace std;
// C++方式
bool MySort(int a, int b)
{
return a < b;
}

void Display(int a)
{
cout << a << " ";
}

// C++泛型
template<class T>
inline bool MySortT(T const& a, T const& b)
{
return a < b;
}

template<class T>
inline void DisplayT(T const& a)
{
cout << a << " ";
}

// C++仿函数
struct SortF
{
bool operator() (int a, int b)
{
return a < b;
}
};
struct DisplayF
{
void operator() (int a)
{
cout << a << " ";
}
};

// C++仿函数模板
template<class T>
struct SortTF
{
inline bool operator() (T const& a, T const& b) const
{
return a < b;
}
};
template<class T>
struct DisplayTF
{
inline void operator() (T const& a) const
{
cout << a << " ";
}
};


int main()
{
// C++方式
int arr[] = { 4, 3, 2, 1, 7 };
sort(arr, arr + 5, MySort);
for_each(arr, arr + 5, Display);
cout << endl;

// C++泛型
int arr2[] = { 4, 3, 2, 1, 7 };
sort(arr2, arr2 + 5, MySortT<int>);
for_each(arr2, arr2 + 5, DisplayT<int>);
cout << endl;


// C++仿函数
int arr4[] = { 4, 3, 2, 1, 7 };
sort(arr4, arr4 + 5, SortF());
for_each(arr4, arr4 + 5, DisplayF());
cout << endl;

// C++仿函数模板
int arr3[] = { 4, 3, 2, 1, 7 };
sort(arr3, arr3 + 5, SortTF<int>() );
for_each(arr3, arr3 + 5, DisplayTF<int>());
cout << endl;

return 0;
}

2020年5月16日 下午11:34

总结:

  1. 移动:可以分为两类:
    1. 移动构造函数
    2. 移动赋值运算
  2. 实现移动的原理:
    1. 使用&&右值引用来实现移动,其实就是一种重载。
    2. 具体的可以查看吴永伟《C++实战》-右值引用到底有什么用

String.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma once
using namespace std;
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
String(String&& other); // 移动构造函数
~String(void); // 析构函数
String& operator= (const String& other); // 赋值函数
String& operator=(String&& rhs)noexcept; // 移动赋值运算符

friend ostream& operator<<(ostream& os, const String &c); // cout输出

private:
char *m_data; // 用于保存字符串
};

String.cpp

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
#include "stdafx.h"


// _CRT_SECURE_NO_WARNINGS

// String 的普通构造函数
String::String(const char *str)
{
if (str == NULL)
{
m_data = new char[1];
if (m_data != NULL)
{
*m_data = '\0';
}
else
{
exit(-1);
}
}
else
{
int len = strlen(str);
m_data = new char[len + 1];
if (m_data != NULL)
{
strcpy(m_data, str);
}
else
{
exit(-1);
}
}
}

// 拷贝构造函数
String::String(const String &other)
{
int len = strlen(other.m_data);
m_data = new char[len + 1];
if (m_data != NULL)
{
strcpy(m_data, other.m_data);
}
else
{
exit(-1);
}
}

// 移动构造函数
String::String(String&& other)
{
if (other.m_data != NULL)
{
// 资源让渡
m_data = other.m_data;
other.m_data = NULL;
}
}


// 赋值函数
String& String::operator= (const String &other)
{
if (this == &other)
{
return *this;
}
// 释放原有的内容
delete[ ] m_data;
// 重新分配资源并赋值
int len = strlen(other.m_data);
m_data = new char[len + 1];
if (m_data != NULL)
{
strcpy(m_data, other.m_data);
}
else
{
exit(-1);
}

return *this;
}

// 移动赋值运算符
String& String::operator=(String&& rhs)noexcept
{
if(this != &rhs)
{
delete[] m_data;
m_data = rhs.m_data;
rhs.m_data = NULL;
}
return *this;
}

// String 的析构函数
String::~String(void)
{
if (m_data != NULL)
{
delete[] m_data;
}
}

ostream& operator<<(ostream& os, const String &c)
{
os << c.m_data;
return os;
}