std::string::size_type num = str.size(); // 用stl库给出的特定类型,不用纠结 int 还是 unsigned int std::vector<double>::size_type vec_size = vec.size();
学会组织计算和数据:
容器,迭代器
std::vector<int>::iterator iter = vec.begin(); std::vector<int>::const_iterator iter = vec.begin(); iter = vec.erase(iter); // 用迭代器实现删除容器中的元素,比索引更安全
思考数据结构合理性,如果不需要 random access(不需要通过索引访问容器内元素),用 list 替代 vector 性能更高。
多用标准库已有的实现:
<cctype> std::isspace()
迭代器,算法;很多容器有类似的操作(find、insert等),提取公共接口形成算法。
STL利用公共接口提供一个算法集合:
for (vector<string>::const_iterator it = str.begin(); it != str.end(); ++it) { ret.push_back(*it); } ret.insert(ret.end(), str.begin(), str.end()); std::copy(str.begin(), str.end(), std::back_inserter(ret));
copy 泛型算法(generic,不属于特定容器)
back_inserter 迭代器适配器(传入一个容器,返回一个迭代器)
利用泛型算法和迭代器适配器实现 split:
std::vector<std::string> split(const std::string &str) { typedef std::string::const_iterator iter_t; std::vector<std::string> ret; iter_t i = str.begin(); while (i != str.end()) { // 忽略前端空格 i = std::find_if(i, str.end(), [](const char &ch) { return !std::isspace(ch); }); // 下一个单词结尾空格 iter_t j = std::find_if(i, str.end(), [](const char &ch) { return std::isspace(ch); }); // 复制 [i, j) if (i != str.end()) { ret.push_back(std::string(i, j)); } i = j; } return ret; }
回文字符串判断:
bool is_palindrome(const std::string &s) { return std::equal(s.begin(), s.end(), s.rbegin()); }
map/reduce :
std::transform() / std::accumulate()
算法作用于容器的元素,而不是容器
顺序容器: vector, list 等
关联容器:map 等
模板函数,模板实例化
数据结构独立性,find(v.begin(), v.end(), x)
对比 v.find(x)
,前者更通用
用迭代器提高适应性:
template <typename Out> void split(const std::string &str, Out out) { typedef std::string::const_iterator iter_t; iter_t i = str.begin(); while (i != str.end()) { // 忽略前端空格 i = std::find_if(i, str.end(), [](const char &ch) { return !std::isspace(ch); }); // 下一个单词结尾空格 iter_t j = std::find_if(i, str.end(), [](const char &ch) { return std::isspace(ch); }); // 复制 [i, j) if (i != str.end()) { *out++ = std::string(i, j); } i = j; } } std::list<std::string> word_list; split(s, std::back_inserter(word_list));
三种内存分配方法:局部、静态、动态
ch11 完整的 Vec 类实现,其中 allocator 部分需要好好理解
频繁使用 new delete 会增加资源开销
allocator
分配一块预备用来存储 T 类型对象的内存块
handle class ,主程序中频繁使用基类、派生类指针,容易出错,通过一个类封装基类指针来隐藏内存分配、释放:
class Core { public: string name(); virtual double grade(); // ... }; class Grad : public Core { public: virtual double grade() override; // ... }; class Student_info { public: Student_info() : ptr_(nullptr) {} ~Student_info() { delete ptr_; } void read(string s) { if (s == "U") { ptr_ = new Core; } else { ptr_ = new Grad; } } string name() { return ptr_->name(); } double grade() { return ptr_->grade(); } private: Core *ptr_; // 封装一个基类指针 };
使用 handle class 时需要注意 handle class 对象的拷贝,如何确定拷贝的是含有基类/子类实例的对象?
class Core { friend class Student_info; protected: virtual Core* clone() const { return new Core(*this); } }; class Grad : public Core { protected: Grad* clone() const { return new Grad(*this); } }; Student_info::Student_info(const Student_info& info) : ptr_(nullptr) { if (info.ptr_ != nullptr) { ptr_ = s.ptr_->clone(); } } Student_info& Student_info::operator=(const Student_info& info) { if (&info != this) { delete ptr_; ptr_ = nullptr; if (info.ptr_ != nullptr) { ptr_ = info.ptr_.clone(); } } return *this; }
注意上面代码的巧妙之处,通过 protected 虚函数 clone 实现实例拷贝,声明 handle class 为友元
通用的 handle class (智能指针雏形):
template <typename T> class Handle { public: Handle() : p_(nullptr) {} Handle(const Handle &that) : p_(nullptr) { if (that.p_) { p_ = new T(*(that.p_)); // clone } } Handle& operator=(const Handle&); ~Handle { delete p_; } Handle(T *p) : p_(p) {} operator bool() { return p_ != nullptr; } T& operator&() const; T* operator->() const; private: T *p_; };
上面的 handle 类总是会进行数据拷贝,可以使用引用计数来记录有多少个对象指向底层对象:
template <typename T> class RefHandle { public: RefHandle() : ref_cnt_(1), p_(nullptr) {} RefHandle(T *p) : ref_cnt_(1), p_(p) {} RefHandle(const RefHandle &h) : ref_cnt_(h.ref_cnt_), p_(h.p_) { ++ref_cnt_; } RefHandle& operator=(const RefHandle &h) { ++*h.ref_cnt_; --*this.ref_cnt_; if (*this.ref_cnt_ == 0) { delete ref_cnt_; delete p_; } ref_cnt_ = h.ref_cnt_; p_ = h.p_; return *this; } ~RefHandle() { --*this.ref_cnt_; if (*this.ref_cnt_ == 0) { delete ref_cnt_; delete p_; } } private: T *p_; size_t *ref_cnt_; };
可以增加一个 make_unique() 方法来取消共享数据:
void RefHandle::make_unique() { if (*ref_cnt_ != 1) { --*ref_cnt_; ref_cnt_ = new size_t(1); p_ = p_ ? new T(*p_) : nullptr; // clone } }
软件工程中的基本思想:所有问题都可以通过引入一个额外的间接层来解决:
tempate <typename T> T* clone(const T* p) { return p->clone(); } // 模板特化 // 对 Vec<char> 类型特化 template <> Vec<char>* clone(const Vec<char> *p) { return new Vec<char>(*p); }