这阵子真是太忙了, 连续做了四个课设。 当然这并不能作为好久没写博客的借口, 没写博客的主要原因只有一个: 懒。 最近又开始回顾C++的语法与特性(据说C++就是一门需要反复回顾的语言),以及学习C++的编程规范。 敲了C++Primer 5th 上的一道典型的练习题,纪念一下这即将过去的2016.
题目描述: 定义你自己版本的 StrBlobPtr, 更新 StrBlob类, 加入恰当的 friend 声明及begin 和 end 成员。
这道题目主要是练习 智能指针 share_ptr 和 weak_ptr。
我的环境: Win10 + VS2015
声明 StrBlob 类和 类StrBlobPtr的文件: StrBlob.h
1 #pragma once 2 #ifndef PRACTICE_STRBLOB_H_ 3 #define PRACTICE_STRBLOB_H_ 4 #include <memory> 5 #include <vector> 6 #include <string> 7 #endif PRACTICE_STRBLOB_H_ 8 9 // 对于 StrBlob 中的友元声明来说,这个前置声明是必要的 10 class StrBlobPtr; 11 class StrBlob { 12 friend class StrBlobPtr; 13 public: 14 typedef std::vector<std::string>::size_type size_type; 15 StrBlob():data(std::make_shared<std::vector<std::string>>()) { } 16 StrBlob(std::initializer_list<std::string>il):data(std::make_shared<std::vector<std::string>>(il)){ } 17 size_type size() const { return data->size(); } 18 bool empty() const { return data->empty(); } 19 // 添加和删除元素 20 void push_back(const std::string &t) { data->push_back(t); } 21 void pop_back(); 22 // 元素访问 23 std::string& front(); 24 std::string& back(); 25 const std::string& front() const; 26 const std::string& back() const; 27 28 // 返回指向首元素和尾元素的 StrBlobPtr 29 StrBlobPtr begin(); 30 StrBlobPtr end(); 31 private: 32 std::shared_ptr<std::vector<std::string>> data; 33 void check(size_type i, const std::string &msg) const; 34 }; 35 36 37 // 对于访问一个不存在元素的尝试, StrBlobPtr抛出一个异常 38 class StrBlobPtr { 39 public: 40 StrBlobPtr(): curr(0) { } 41 StrBlobPtr(StrBlob &a, size_t sz = 0): 42 wptr(a.data), curr(sz) { } 43 std::string& deref() const; 44 StrBlobPtr& incr(); // 前缀递增 45 private: 46 // 若检查成功, check返回一个指向 vector 的 shared_ptr 47 std::shared_ptr<std::vector<std::string>> 48 check(std::size_t i, const std::string& msg) const; 49 // 保存一个 weak_ptr, 意味着底层 vector 可能会被销毁 50 std::weak_ptr<std::vector<std::string>> wptr; 51 std::size_t curr; // 在数组中的当前位置 52 }; 53 54 55 56
实现 StrBlob 类和 类StrBlobPtr的文件: StrBlob.cpp
1 #include "stdafx.h" 2 #include "StrBlob.h" 3 4 //----------------------------------- 5 // 类 StrBlob 的实现 6 //----------------------------------- 7 void StrBlob::pop_back() 8 { 9 check(0, "pop_back on empty StrBlob"); 10 data->pop_back(); 11 } 12 13 std::string& StrBlob::front() 14 { 15 // 如果 vector 为空, check 会抛出一个异常 16 check(0, "front on empty StrBlob"); 17 return data->front(); 18 } 19 20 std::string& StrBlob::back() 21 { 22 // 如果 vector 为空, check 会抛出一个异常 23 check(0, "back on empty StrBlob"); 24 return data->back(); 25 } 26 27 const std::string& StrBlob::front() const 28 { 29 check(0, "front on empty StrBlob"); 30 return data->front(); 31 } 32 33 const std::string& StrBlob::back() const 34 { 35 check(0, "back on empty StrBlob"); 36 return data->back(); 37 } 38 39 void StrBlob::check(size_type i, const std::string& msg) const 40 { 41 if (i >= data->size()) 42 throw std::out_of_range(msg); 43 } 44 45 std::string & StrBlobPtr::deref() const 46 { 47 auto p = check(curr, "dereferemce past end"); 48 return (*p)[curr]; // (*p) 是对象所指向的 vector 49 } 50 51 52 // 必须在 StrBlobPtr 定义之后进行定义 53 StrBlobPtr StrBlob::begin() 54 { 55 return StrBlobPtr(*this); 56 } 57 58 // 必须在 StrBlobPtr 定义之后进行定义 59 StrBlobPtr StrBlob::end() 60 { 61 auto ret = StrBlobPtr(*this, data->size()); 62 return ret; 63 } 64 65 66 //---------------------------------------- 67 // 类 StrBlobPtr 的实现 68 //---------------------------------------- 69 StrBlobPtr & StrBlobPtr::incr() 70 { 71 // 如果 curr 已经指向容器的尾后位置, 就不能递增它 72 check(curr, "increment past end of StrBlobPtr"); 73 ++curr; // 推进当前位置 74 return *this; 75 } 76 77 std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::size_t i, const std::string & msg) const 78 { 79 auto ret = wptr.lock(); // 检查 vector 是否还存在 80 if (!ret) 81 throw std::runtime_error("unbound StrBlobPtr"); 82 if (i >= ret->size()) 83 throw std::out_of_range(msg); 84 85 return ret; // 否则, 返回指向 vector 的 shared_ptr 86 }
用于测试的文件: practice_12.19.cpp
1 // practice_12.19.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include "StrBlob.h" 6 #include <iostream> 7 using namespace std; 8 9 int main() 10 { 11 StrBlob blob{ "So", "Fun", "So", "Good" }; 12 StrBlobPtr blobPtr(blob); 13 cout << blob.front() << endl; 14 blobPtr.incr(); 15 cout << blobPtr.deref() << endl; 16 blobPtr = blob.begin(); 17 cout << blobPtr.deref() << endl; 18 cout << blob.back() << endl; 19 return 0; 20 }
运行结果截图:
这道题目是很经典的,但是很不幸, C++Primer 5th (中文版)上,却把这道题目的代码写错了一丢丢。
回顾一下这道题涉及的主要姿势:
一般程序使用动态内容的原因
- 程序不知道自己需要使用多少对象
- 程序不知道所需对象的准确类型
- 程序需要在多个对象间共享数据(这个例子就是共享数据的问题)
友元的相关知识
- 友元类一定要事先声明(或定义)
- 需要用到友元类中的具体成员时,必须保证友元类已经定义。
列表初始化
头文件定义规范
时间: 2024-10-30 09:18:48