c++ primer reading notes

Table of Contents

1 第1章 开始

2 第一部分 C++基础

2.1 第2章 变量和基本类型

2.1.1 基本内置类型

2.1.2 变量

2.1.3 复合类型

2.1.4 const限定符

   TEST (TestConst, ConstVar) {
       const int var = 10;
       int* ptr = const_cast<int*>(&var);
       *ptr = 20;

       std::cout << var << " " << *ptr << std::endl; //此处编译器使用常量直接进行了替代
       EXPECT_EQ (&var, ptr) << "var 与 ptr地址不一致" << std::endl;
       EXPECT_EQ (var, 10) << "var is " << var << std::endl;
       EXPECT_EQ (*ptr, 20) << "ptr is " << *ptr << std::endl;

       struct Test {
           int var_;
           Test():var_(10){ }
       };
       const Test t;
       auto p_var = &Test::var_; // 数据成员指针
       auto ptr2 = const_cast<int*>(&(t.*p_var));
       *ptr2 = 20;

       std::cout << t.var_ << " " << *ptr2 << std::endl;
       EXPECT_EQ (&t.var_, ptr2) << "t.var_ 与 ptr2 地址不一致" << std::endl;
       EXPECT_EQ (t.var_, 20) << "t.var_ is " << t.var_ << std::endl;
       EXPECT_EQ (*ptr2, 20) << "ptr2 is " << ptr2 << std::endl;
   }

2.1.5 处理类型

2.1.6 自定义数据结构

2.2 第3章 字符串、向量和数组

2.3 第4章 表达式

2.4 第5章 语句

2.5 第6章 函数

2.6 第7章 类

3 第二部分 C++ 标准库

3.1 第8章 IO库

3.1.1 IO类

IO对象无拷贝或赋值

L279:我们不能对IO对象进行拷贝。读写一个IO对象会改变其状态,因此传递和返回的引用不能是const。

条件状态
#include <iostream>

int main(int argc, char *argv[])
{
	std::cout << std::cout.bad() << std::endl;

    return 0;
}

3.1.2 文件输入输出

   #include <iostream>
   #include <fstream>
   #include <sstream>

   #include <vector>
   #include <string>

   using namespace std;

   int TestStream() {
       ofstream outFile("hello.txt", ofstream::out);
       if (outFile) {
           for (int i = 0; i < 100; ++i) {
               outFile << "This is Line " << i << endl;
           }
           outFile.close();
       }


       ifstream inFile("hello.txt", ifstream::in);
       if (inFile) {
           string line;
           while (getline(inFile, line)) {
               cout << line << endl;
           }
           inFile.close();
       }

       return 0;
   }

3.1.3 string流

3.2 第9章 顺序容器

3.2.1 顺序容器概述

L292:顺序容器都是考虑到两个方面的折中体现:1.添加或删除容器的代价。2.非顺序访问容器中元素的代价。

3.2.2 容器库概览

L294:如果顺序容器中要保存的元素类型没有默认构造函数,我们在定义的时候需要手动传递一个元素初始化器。

vector<nodefault> v(n,init);
迭代器
容器类型成员

L298:通过类型别名,我们可以在不了解容器中元素的情况下使用它。一般用在泛型编程中比较多。

begin和end成员
容器的定义和初始化

L300:当传递迭代器参数来拷贝一个范围时,不要求容器类型相同,而且新荣器和原容器的元素类型也可以不同,只要能将容器的元素类型进行转化即可。

L301:标准array具有固定大小,所以定义的时候除了指定元素类型,还要指定容器大小。

赋值和swap

L302:赋值运算符左右两侧的运算对象类型要相同。array不支持assign进行赋值,也不支持花括号包围的值列表进行赋值。

容器大小操作

L304:forward_list不支持size。

关系操作符

**

L303:调用swap会导致迭代器、指针或引用失效。统一使用非成员版本的swap是一个好习惯。

3.2.3 顺序容器操作

向顺序容器添加元素

L306: 不支持以下操作的容器:

  • push_back: array、forward_list
  • push_front: vector、array
  • insert:array

注意:insert插入到迭代器指示的位置之前,并返回指向插入的第一个元素的迭代器。

访问元素

L310:访问成员函数front、back、at返回的是引用。

删除元素

L311:不支持以下操作的容器:

  • pop_front:vector、string
  • pop_back:forward_list

注意:erase(p)删除p所指定的元素,返回一个指向被删除元素之后元素的迭代器。

特殊的forward_list操作
改变容器的大小
容器操作可能使迭代器失效

3.2.4 vector对象是如何增长的

3.2.5 额外的string操作

3.2.6 容器适配器

L329:本质上、一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物。

默认情况下,stack和queue是基于deque实现的,priority_queue是基于vector实现的。

优先队列使用举例:1


3.3 第10章 泛型算法

3.3.1 概述

3.3.2 初始泛型算法

3.3.3 定制操作

3.3.4 再探迭代其

3.3.5 泛型算法结构

3.4 第11章 关联容器

L374:无序容器使用哈希函数来组织元素。

3.4.1 使用关联容器

3.4.2 关联容器概述

L376:关联容器不支持顺序容器的位置相关的操作,因为关联容器是根据关键字存储的。

定义关联容器
关键字类型的要求

L378:有序容器关键字类型必须定义元素比较方法。

multiset<key,decltype(compare)*> s; //注意不要忘记*指定函数指针

3.4.3 关联容器操作

关联容器和迭代器

L383:我们通过不对关联容器使用泛型算法。关键是const这一特性意味着不能将关联容器传递给修改或重排容器元素的算法。

L383:对关联容器使用泛型搜索算法(快排)是一个坏主意,应该使用关联容器的find成员函数。

L383:在实际编程中,如果真要对一个关联容器使用算法,它经常作为一个源序列或者目的位置。copy算法。

添加元素

L384:对于不包含重复关键字的容器,insert函数返回的是一个pair,first是一个指向给定关键字元素的迭代器,second成员是bool,表明插入成功还是已在容器中。

删除元素
c.erase(k) //返回删除元素的数量
c.erase(p) c.erase(b,e) //删除指定(范围)元素,返回最后一个指向最后一个被删除元素之后位置的迭代器
map的下标操作

L387:与其他下标运算符不同的是,如果关键字并不在map中,会为它创建一个元素并插入到map中。 *但是at函数不会执行插入操作,而是抛出out_of_range*的异常。 注意map下标操作符返回 mapped_type ,迭代器解引用返回 value_type

访问元素
c.find(k)
c.cout(k)
c.lower_bound(k)
c.upper_bound(k)
c.equal_range(k)

3.4.4 无序容器



L395:无序容器的性能依赖与哈希函数的质量和桶的数量和大小。

3.5 第12章 动态内存

3.5.1 动态内存与智能指针

实现智能指针类
   // 知识点:常见的智能指针操作
   namespace phenix3443 {
       template <typename T>
       class SmartPtr {
       public:
           SmartPtr (T* p = nullptr) :ptr (p), use (new size_t (0)) { p ? *++use : ; }
           SmartPtr (const SmartPtr& rsh);
           SmartPtr & operator=(const SmartPtr& rsh);
           ~SmartPtr ();
           T& operator*() const { return *ptr; }
           //知识点:重载箭头操作符,注意返回值。
           T* operator->() const { return ptr; }
           //知识点:类类型转换
           explicit operator bool () const { return nullptr == ptr; }
           bool unique () { return (1 == *use) ? true : fasle; }
           size_t count () { return *use; }
           T* get () { return ptr; }

       private:
           T* ptr;
           size_t* use;
       };

       //知识点:在外部定义类模板的成员函数
       template<typename T>
       inline  SmartPtr<T>::SmartPtr (const SmartPtr& rsh) {
           ++*rsh.use;
           use = rsh.use;
           ptr = rsh.ptr;
       }

       template<typename T>
       inline SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr& rsh) {
           if ( --*use == 0 ) {
               delete ptr;
               delete use;
           }
           use = rsh.use;
           ++*use;
           ptr = rsh.ptr;
           return *this;
       }

       template<typename T>
       inline SmartPtr<T>::~SmartPtr () {
           if ( --*use == 0 ) {
               delete ptr;
               delete use;
               // 知识点:预防野指针
               ptr = NULL;
               use = NULL;
           }
       }
   }

3.5.2 动态数组

3.5.3 使用标准库:文本查询程序

4 第三部分 类设计者的工具

4.1 第13章 拷贝控制

4.2 第14章 重载运算与类型转换

4.2.1 基本概念

4.2.2 输入和输出运算符

4.2.3 算术和关系运算符

4.2.4 赋值运算符

4.2.5 下标运算符

4.2.6 递增和递减运算符

4.2.7 成员访问运算符

4.2.8 函数调用运算符

    #include <iostream>
    #include <map>
    #include <functional>
    #include <gtest\gtest.h>

    namespace phenix3443 {
        int add (int a, int b) {
            return a + b;
        }

        class divide {
        public:
            int operator() (int a, int b) {
                return a / b;
            }
        };

        auto mod = [](int a, int b) { return a%b; };


        std::map<std::string, std::function<int (int, int)>> binops = {
            { "+", add },
            { "-", std::minus<int> () },
            { "/", divide () },
            { "*", [](int a, int b) {return a*b; } },
            { "%", mod }
        };
        TEST(TestFunction,Calulator){
            EXPECT_EQ (30, binops["+"] (10, 20));
        }
    }

4.2.9 重载、类型转换与运算符

4.3 第15章 面向对象程序设计

4.4 第16章 模板与泛型编程

5 第四部分 高级主题

5.1 第17章 标准库特殊设施

5.2 第18章 用于大型程序的工具

5.2.1 异常处理

5.2.2 命名空间

  #include <gtest\gtest.h>

  //测试命名空间
  namespace phenix3443 {
      namespace NS {
          class Quote { };
          void display (Quote&) { std::cout << "function in ns namespace" << std::endl; }
      }
      TEST (TestNameSpace, TestFunctionFind) {
          //测试嵌套命名空间中函数蚕食是类对象的查找过程。
          display (NS::Quote ());
      }

      namespace A { int i = 0, j = 0; }
      int f () {
          int i = 1;
          using namespace A;
          std::cout << "f() i = " << i << std::endl;
          return i;
      }
      int g () {
          using A::i;
          std::cout << "g() i = " << i << std::endl;
          return i;
      }
      TEST (TestNameSpace, TestUsing) {
          //测试using声明和using指示作用域的差别
          EXPECT_EQ (1, f ());
          EXPECT_EQ (0, g ());
      }
  }

5.2.3 多重继承与虚继承

5.3 第19章 特殊工具与技术

5.3.1 控制内存分配

定位new修改内存数据
  #include <iostream>
  #include <gtest\gtest.h>

  //测试new和delete
  namespace phenix3443 {
      TEST (NewDelete, PlaceNew) {
          //测试定位new修改制定内存数据
          int a = 10;
          int* ps = new(&a) int (20);
          EXPECT_EQ (20, a);
      }
  }

5.3.2 运行时类型识别

    #include <iostream>
    #include <gtest\gtest.h>

    //测试RTTI,通过RTTI如何定义类的相等操作。
    namespace phenix3443 {
        class Base {
        public:
            Base (int i = 0) :b_ (i) { }
        protected:
            virtual bool Equal (Base& rsh) { return b_ == rsh.b_; }
        private:
            int b_;

            friend bool operator== (Base& a, Base& b);
        };
        class Derived : public Base {
        public:
            Derived (int j=1) :d_ (j) { }
        protected:
            virtual bool Equal (Base& rsh) {
                Derived& tmp = dynamic_cast<Derived&>(rsh);
                return d_ == tmp.d_;
            }
        private:
            int d_;
        };
        bool operator==(Base& a, Base& b) {
            return (typeid(a) == typeid(b) && a.Equal (b));
        }

        TEST (TestRTTI, ClassEqual) {
            Base b;
            Derived d1, d2;
            std::cout << "Class Base's name is " << typeid(b).name() << std::endl;
            std::cout << "Class Derive's name is " << typeid(d1).name() << std::endl;
            EXPECT_TRUE (typeid(b).before (typeid(d1)));
            EXPECT_FALSE (b == d1);
            EXPECT_TRUE(d1 == d2);
            Base& br = d1;
            EXPECT_TRUE (br == d1);
        }
    }

5.3.3 枚举类型

5.3.4 类成员指针

    //测试函数成员指针
    namespace phenix3443 {
        class Screen {
        public:
            Screen () { }
            Screen (const std::initializer_list<std::string>& il) :content_ (il) { }
            char get () const {
                return content_[cursor_[0]][cursor_[1]];
            }
            Screen& Home () {
                cursor_ = { 0,0 };
                return *this;
            }
            Screen& End () {
                cursor_ = { content_.size () - 1,content_.back ().size () - 1 };
                return *this;
            }
            Screen& Forward () {
                if ( cursor_[1] < content_[cursor_[0]].size () - 1 ) {
                    ++cursor_[1];
                }
                else {
                    if ( cursor_[0] < content_.size () - 1 ) {
                        ++cursor_[0];
                        cursor_[1] = 0;
                    }
                }
                return *this;
            }
            Screen& Back () {
                if ( cursor_[1] > 0 ) {
                    --cursor_[1];
                }
                else {
                    if ( cursor_[0] > 0 ) {
                        --cursor_[0];
                        cursor_[1] = content_[cursor_[0]].size () - 1;
                    }
                }
                return *this;
            }
            enum Direction { HOME, FORWARD, BACK, END };
            using Action = Screen& (Screen::*)();
            Screen& Move (Direction d) {
                return (this->*menu_[d])();
            }
        private:
            std::vector<std::string> content_;
            std::array<size_t, 2> cursor_ = { 0,0 };
            static Action menu_[];
        };
        Screen::Action Screen::menu_[] = { &Screen::Home,&Screen::Forward,&Screen::Back,&Screen::End };

        TEST (ClassMemPoint, ScreenTest) {
            Screen text = { "first","second","third" };
            EXPECT_EQ ('f', text.get ());
            text.Move (Screen::FORWARD);
            EXPECT_EQ ('i', text.get ());
        }
    }

5.3.5 嵌套类

5.3.6 union

5.3.7 局部类

5.3.8 固有的不可移植的特性

Footnotes:

Author: lsl

Created: 2017-08-30 三 16:06

Emacs 25.2.2 (Org mode 8.2.10)

Validate