C++ Primer Plus读书感悟

C++ Primer Plus 读书感悟

1. vector的扩容机制

在这里插入图片描述

总结:0~4都是增加一个扩容一个元素,4以后当容量不够时扩容至1.5倍 (这个结论仅限VS下)

如果使用GCC的话会发现扩容的倍数将会是2倍。

2. 智能指针

  1. auto_ptr 和 unique_ptr 是所有权的归属发生了转换,share_ptr 是在内部进行了一个引用计数,当计数等于0时才调用delete

  2. 智能指针的定义

    void printInt(int num)
    {
    	int* a = new int(num);
    	//*a = num;
    	printf("%p, %d\n", a, *a);
    	shared_ptr<int> ps;
    	ps = shared_ptr<int>(a);
    	printf("%p, %d\n", ps, *ps);
    	cout.operator<< (*ps) << endl;
    }
    
    void printString(string str)
    {
    	shared_ptr<string> ps(new string("hello world"));
    	cout << *ps << endl;
    	printf("%s\n", ps->c_str());
    }
    

    注意:打印智能指针的解引用的值时一定要使用cout,因为printf是不可以打印一个类的值的。

  3. unique_ptr 有自己的保护机制,如果使用一个指针直接用=号给另一个指针赋值时,此时是不允许的,因为会发生悬挂指针的问题,原有的指针会发生因为所有权的转移而无法使用的问题。那么如果想进行赋值的话,要使用C++11的move()函数

    unique_ptr<string> ps1, ps2;
    ps1 = unique_ptr<string> (new string("hello world"));
    //ps2 = ps1;    //不允许
    ps2 = move(ps1);
    

    使用move() 函数后,此时这两个指针都可以访问到正确的结果。

  4. 如何选择智能指针

    1. 如果程序要使用多个指向同一个对象的指针,使用share_ptr

    2. 如果不需要多个指针指向同一个对象,可以使用unique_ptr,当一个函数使用new分配内存,那么它的返回值可以使用unique_ptr来进行返回值的接收,同时可以将unique_ptr变为该地址的所有者,并管理内存的释放。

      unique_ptr<int> make_int(int num)
      {
          return unique_ptr<int>(new int(num));
      }
      

3. STL中算法的使用

  1. next_permutation(xxx.begin(), endxxx.end),用于获得全排列的下一种排列方式

4. 文件操作(流)

  1. 读文件使用 ifstream

  2. 写文件使用 ofstream

  3. fstream继承与以上两个类,可以同时进行读和写的操作

    fstream fins("abc.txt", ios_base::app | ios::in || ios::out);
    char ch;
    while (fins.read((char*)&ch, sizeof ch))
    {
        cout << ch;
    }
    cout << endl;
    

    以上代码使用了 .read((char*)&ch, sizeof ch) 的方法进行了文件内容的读出

5. 字符串流

  1. istringstream 用来输出字符串,它的定义必须要指定初始化的字符串,输出后的字符串不包含空格

  2. ostringstream 用来输入字符串,当输入打算结束时,调用 .str() 方法可以返回其中的所有字符串

  3. stringstream 可以同时输入和输出字符串,特点是也没有空格。

    // ostringstream 用来向其中输入字符串,并通过.str()方法进行返回一个字符串
    ostringstream ostr;
    ostr << "hello world!";
    ostr << "alin";
    ostr << " loves c++!";
    cout << ostr.str() << endl;
    /*ostr << "abcdef";
    	cout << ostr.str() << endl;*/
    
    // istringstream 用来输出字符串,在定义时要指定字符串作为初始化参数
    // istringstream 会屏蔽空格
    istringstream istr("hello world! I love c++!");
    string output;
    while (istr >> output)
    {
        cout << output << " ";
    }
    cout << endl;
    
    stringstream ss;
    ss << "hello world";
    string str;
    while (ss >> str)
    {
        cout << str << endl;
    }
    

6. C++11新特性

1. 初始化方式

使用{} 可以完成数组或者变量的初始化。

2. 声明

  1. auto 自动类型推导,没什么可说的了

  2. decltype:指定一个变量的类型和一个变量相同

    double x;
    int n;
    decltype (x*n) q;   // q:double
    decltype (&n) p;    // p:int*
    decltype ((n)) m;   // m:int &
    

    这种定义的方式在模板种非常有用,因为在调用时才知道变量的类型,所以需要将变量的类型设置成相同的。

3. 有关类

支持在类内给类成员进行初始化

4. 有关模板和STL

  1. 增强for循环

    for(auto &num : nums)
    {
        ……
    }
    
  2. 新增容器

    • unordered_xxx

    • forward_list 单向链表

    • array 在定义时要指定固定的元素个数

      std::array<int, 360> ar;
      
  3. 新的STL方法

    • cbegin(),cend() 表示const,只读不可修改
    • crbegin(), crend() 只读

5. 右值引用与移动语义

移动语义:用于消除一些额外的中间变量的产生,以提高程序执行的效率。

要让移动语义发生,要有两个步骤:

第一步:要让编译器知道何时使用移动语义。

Useless two = one;  		   // 调用拷贝构造函数
Useless three = (one + two);   // 移动语义右值引用,直接转移one+two到所有权到three

第二步:编写移动语义对应的函数,处理其中的逻辑。

  • 如果想让一个普通的对象也使用移动语义的方式,那么就需要使用强制移动

    #include <utility>
    Useless four = std::move(one);    //此时会调用右值引用的函数
    

右值语义的意义在于编写能够利用右值引用实现库代码,例如:STL包含移动构造函数。

6. 新的类的功能

  1. 特殊的成员函数:

    // 提供了默认的移动构造函数和移动运算符
    Someclass::Someclass(Someclass &&);
    Someclass& Someclass::operator(Someclass &&);
    
  2. 与以前的类的方法定义规则相同,如果只显示定义了移动构造函数,那么默认构造函数、拷贝构造函数都不会被自动定义出来,可以使用 函数声明=default; 的方法来进行默认的函数的构造。

    class Someclass
    {
    public:
        Someclass(Someclass &&);
        Someclass() = default;
        Someclass(const Someclass &) = default;
        Someclass & operator=(const Someclass &) = default;
        ...
    };
    
  3. 如果想要禁用某个函数,那么就使用 类成员函数声明=delete

    Someclass(const Someclass &) = delete;
    

注意:关键字default只能用于6个特殊的成员函数,delete可以作用于全部的类的成员函数。

  1. 委托构造函数

  2. 继承构造函数

    class C1
    {
    public:
        int fn(int j){...}
        ...
    };
    class C1
    {
    public:
        using C1::fn;    //如果C1中的fn有重载的版本的话,也一并继承
        ...
    };
    
  3. 管理虚方法

    1. override
    • 如果当子类重写虚方法的时候没有完全按照虚函数的定义书写,那么将会出现隐藏了原有的虚函数的问题,一些函数的调用将无法使用。
    • 在重写虚函数的函数后面加上override,如果重写的函数与虚函数不匹配的话,将会报错
    virtual void f(char* ch) const override {
        ...
    }
    
    1. final
    • 如果想禁止派生类覆盖特定的虚方法,可以在参数列表后面加上final
    virtual void f(char ch) const final {
    	...
    }
    

7. lambda表达式

  1. lambda表达式的好处:可以直接在函数的调用处写出处理的逻辑,坏处就是无法进行复用。给lambda表达式添加一个定义即可对它进行命名和复用

    vector<int> n1 = {1, 2, 3, 4, 5, 6};
    vector<int> n2 = {1, 2, 3, 4, 5, 6};
    auto mod = [](){return x % 3 == 0;};
    count1 = std::count_if(n1.begin(), n1.end(), mod);
    count2 = std::count_if(n2.begin(), n2.end(), mod);
    
  2. []中的符号

    • = :代表可以访问作用域内的任何动态变量,其名称可以放在小括号内,传递形式是值传递
    • & : 作用同上,传递形式是引用传递

8. 可变参模板函数

使用... args来表示可变参

template<typename T>
void show_list(const T& value)
{
    std::cout << value << "\n";
}

template<typename T, typename... Args>
void show_list(const T& value, const Args&... args)
{
    std::cout << value << ",";
    show_list(args...);
}

注意如上函数的声明,函数二的第一个参数是为了对show_list的递归调用每次消除一个参数,如果不这样消除参数,那么将会一直循环递归无法停下。然后还需要再定义一个只有一个参数的函数用于进行最终值的接收。

函数调用的时候可以传递不同类型的参数。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值