文章出處

 

  這陣子真是太忙了, 連續做了四個課設。 當然這并不能作為好久沒寫博客的借口, 沒寫博客的主要原因只有一個: 。 最近又開始回顧C++的語法與特性(據說C++就是一門需要反復回顧的語言),以及學習C++的編程規范。 敲了C++Primer 5th 上的一道典型的練習題,紀念一下這即將過去的2016.

 

  題目描述: 定義你自己版本的 StrBlobPtr, 更新 StrBlob類, 加入恰當的 friend 聲明及begin 和 end 成員。

  這道題目主要是練習 智能指針 share_ptrweak_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 //    必須在 StrBlobPtr 定義之后進行定義
46 StrBlobPtr StrBlob::begin()
47 {
48     return StrBlobPtr(*this);
49 }
50 
51 // 必須在 StrBlobPtr 定義之后進行定義
52 StrBlobPtr StrBlob::end()
53 {
54     auto ret = StrBlobPtr(*this, data->size());
55     return ret;
56 }
57 
58 
59 //----------------------------------------
60 //         類 StrBlobPtr 的實現 
61 //----------------------------------------
62 
63 std::string & StrBlobPtr::deref() const
64 {
65     auto p = check(curr, "dereferemce past end");
66     return (*p)[curr];    // (*p) 是對象所指向的 vector 
67 }
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 (中文版)上,卻把這道題目的代碼寫錯了一丟丟。

 

回顧一下這道題涉及的主要姿勢:

一般程序使用動態內存的原因

  1. 程序不知道自己需要使用多少對象
  2. 程序不知道所需對象的準確類型
  3. 程序需要在多個對象間共享數據(這個例子就是共享數據的問題)

友元的相關知識

  1. 友元類一定要事先聲明(或定義)
  2. 需要用到友元類中的具體成員時,必須保證友元類已經定義。

 

列表初始化

 

頭文件定義規范

google 的 C++ 編碼規范之頭文件

 頭文件
通常,每一個.cc 文件(C++的源文件)都有一個對應的.h 文件(頭文件) ,也有一些例外,如單元測試代
碼和只包含main()的.cc 文件。
正確使用頭文件可令代碼在可讀性、文件大小和性能上大為改觀。
下面的規則將引導你避免使用頭文件時的各種麻煩。
1. #define 保護
所有頭文件都應該使用#define 防止頭文件被多重包含(multiple inclusion) ,命名格式為:
<PROJECT>_<PATH>_<FILE>_H_
為保證唯一性,頭文件的命名應基于其所在項目源代碼樹的全路徑。例如,項目 foo 中的頭文件
foo/src/bar/baz.h 按如下方式保護:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
2. 頭文件依賴
使用前置聲明(forward declarations)盡量減少.h 文件中#include 的數量。
當一個頭文件被包含的同時也引入了一項新的依賴(dependency) ,叧要該頭文件被修改,代碼就要重新
編譯。如果你的頭文件包噸了其他頭文件,這些頭文件的任何改變也將導致那些包含了你的頭文件的代碼
重新編譯。因此,我們應該盡量少的包含頭文件,尤其是那些包含在其他頭文件中的。
使用前置聲明可以顯著減少需要包含的頭文件數量。舉例說明:頭文件中用到類 File,但不需要訪問 File
的聲明,則頭文件中只需前置聲明 class File;無需#include "file/base/file.h"。
在頭文件如何做到使用類 Foo 而無需訪問類的定義?
1) 將數據成員類型聲明為 Foo *或 Foo &;
2) 參數、返回值類型為 Foo 的函數只是聲明(但不定義實現) ;
3) 靜態數據成員的類型可以被聲明為 Foo,因為靜態數據成員的定義在類定義之外。
另一方面,如果你的類是 Foo 的子類,或者含有類型為 Foo 的非靜態數據成員,則必須為之包含頭文件。
有時,使用指針成員(pointer members,如果是 scoped_ptr 更好)替代對象成員(object members)
的確更有意義。然而,返樣的做法會降低代碼可讀性及執行效率。如果僅僅為了少包含頭文件,還時不要
這樣替代的好。
當然,.cc 文件無論如何都需要所使用類的定義部分,自然也就會包含若干頭文件。
注:能依賴聲明的就不要依賴定義。

 

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()