1. 程式人生 > >C++Primer習題第十二章

C++Primer習題第十二章

我終於快要跟上我之前看書的進度了!!!感覺做題做了好久。。。

練習12.1:在此程式碼的結尾,b1和b2各包含多少個元素?

StrBlob b1;

{

StrBlob b2 = { "a", "an", "the" };

b1 = b2;

b2.push_back( "about" );

}

b1有4個元素。

b2被銷燬。但是元素底層元素並沒有被銷燬。

練習12.2:編寫你自己的StrBlob類,包含const版本的front和back。

一開始我忘了包含一系列的標頭檔案,導致測試這個StrBlob類的時候,編譯不通過:主要是說什麼vector不是一個型別啊,make_shared不知道啥玩意兒啊之類的錯誤資訊。

所以別忘了包含標頭檔案哦。

#ifndef STRBLOB_H_INCLUDED
#define STRBLOB_H_INCLUDED
#include<vector>
#include<string>
#include<stdexcept>
#include<initializer_list>
#include<memory>

using namespace std;
class StrBlob{
public:
    //建構函式
    StrBlob();
    StrBlob( initializer_list<string> il );
    //容器大小訪問
    vector<string>::size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    //新增和刪除元素
    void push_back( const std::string &str ) { data->push_back( str ); }
    void pop_back();
    //元素訪問
    string& front();
    const string& front() const;
    string& back();
    const string& back() const;

private:
    std::shared_ptr<std::vector<std::string>> data;
    void check( vector<string>::size_type i, const std::string &msg ) const ;

};


StrBlob::StrBlob(): data( make_shared<vector<string>>() ) { }
StrBlob::StrBlob(initializer_list<string> il): data( make_shared<vector<string>> (il) ) { }


void StrBlob::check( vector<string>::size_type i, const std::string &msg ) const
{
    if( i >= data->size() )
        throw out_of_range( msg );
}

void StrBlob::pop_back()
{
    check( 0, "pop_back on empty StrBlob" );
    data->pop_back();
}

string& StrBlob::front()
{
    check( 0, "front on empty StrBlob" );
    return data->front();
}

const string& StrBlob::front() const
{
    check( 0, "front on empty StrBlob");
    return data->front();
}

string& StrBlob::back()
{
    check( 0, "back on empty StrBlob" );
    return data->back();
}

const string& StrBlob::back() const
{
    check( 0, "back on empty StrBlob" );
    return data->back();
}


#endif // STRBLOB_H_INCLUDED


練習12.3:StrBlob需要const版本的push_back()和pop_back()嗎?如果需要,新增進去。否則,解釋為什麼不需要。

不需要。因為常量的StrBlob物件是不應被允許修改vector物件內容的。

練習12.4:在我們的check函式中,沒有檢查i是否大於0。為什麼可以忽略這個檢查?

我們將check定義為私有成員函式,也就是,他只會被StrBlob類的成員函式呼叫,而不會被使用者程式呼叫。因此,我們很容易地保證傳遞給它的i值符合要求,而不必進行檢查。

練習12.5:我們未編寫接受一個initializer_list explicit引數的建構函式。討論這個設計策略的優點和缺點。

(標準)

答:

未編寫接受一個初始化列表引數的顯示建構函式,意味著可以進行列表向StrBlob的隱式型別轉換,也就是,在需要StrBlob的地方(如函式的引數),可以使用列表進行替代。而且,可以進行拷貝形式的初始化(如賦值)。這令程式編寫更為簡單方便。

但這種隱式轉換並不總是好的。例如,列表中可能並非都是合法的值。再如,對於接受StrBlob的函式,傳遞給它一個列表,會建立一個臨時的StrBlob物件,用列表對其初始化,然後將其傳遞給函式,當函式完成後,此物件將被丟棄,再也無法訪問了。對於這些情況,我們可以定義顯式的建構函式,禁止隱式類型別轉換。

練習12.6:編寫函式,返回一個動態分配的int的vector。將此vector傳遞給另一個函式,這個函式讀取標準輸入,將讀入的值儲存在vector元素中。再將vector傳遞給另外一個函式,列印讀入的值。記得在恰當的時刻delete vector 。

#include<iostream>
#include<vector>

using namespace std;

vector<int> *new_vector( void );
void read( vector<int> *pv );
void print( vector<int> *pv );

int main()
{
    auto pv = new_vector();
    if( pv == nullptr ){
        cerr << "建立動態分配的vector失敗!";
        return -1;
    }
    cout << "請輸入一個int的序列:" << endl;
    read( pv );
    cout << endl;
    print( pv );

    delete pv;
    pv = nullptr;
    return 0;
}

vector<int> *new_vector( void )
{
    return new ( nothrow) vector<int>;
}

void read( vector<int> *pv )
{
    int i;
    while( cin >> i )
        pv->push_back( i );
}

void print( vector<int> *pv )
{
    cout << "容器中的資料是:" << endl;
    for( const auto &i : *pv )
        cout << i << " ";
    cout << endl;
}

練習12.7:重做上一題,這次使用share_ptr而不是內建指標。
#include<iostream>
#include<memory>
#include<vector>

using namespace std;

shared_ptr<vector<int>> new_vector( void );
void read( shared_ptr<vector<int>> );
void print( shared_ptr<vector<int>> );

int main()
{
    auto spv = new_vector();
    cout << "請輸入一個int的序列:" << endl;
    read( spv );
    cout << endl;
    print( spv );

    return 0;
}

shared_ptr<vector<int>> new_vector( void )
{
    return make_shared<vector<int>>();
}

void read( shared_ptr<vector<int>> pv )
{
    int i;
    while( cin >> i )
        pv->push_back( i );
}

void print( shared_ptr<vector<int>> pv )
{
    cout << "容器中的資料是:" << endl;
    for( const auto &i : *pv )
        cout << i << " ";
    cout << endl;
}

練習12.8:下面的函式是否有錯誤?如果有,解釋錯誤的原因。

bool b() {

int* p = new int;

//...

return p;

}

程式的意圖是:

當分配記憶體成功,則new返回一個合法指標轉換為布林值為true

但若是new分配記憶體失敗,會丟擲一個異常。而不是返回一個nullptr。轉換為false。

修改為:

int* p = new ( nothrow ) int;

練習12.9:解釋下面程式碼執行的結果。

int *q = new int(42), *r = new int(100);// 動態分配,q指向一個值為42的int,r指向一個值為100的int。

r = q;//令r指向q。

原先r指向的那塊記憶體依然存在,但是無法再訪問了。(記憶體洩漏)

------------------------------------------------------------------------------------------------------

auto q2 = make_shared_ptr<int>(42), r2 = make_shared_ptr<int>(100);//智慧指標q2指向值為42的int

//智慧指標r2指向值為100的int

r2 = q2;//r2指向q2,r2原先指向的記憶體被釋放。

練習12.10:下面的程式碼呼叫了第413頁中定義的process函式,解釋此呼叫是否正確。如果不正確,應如何修改?

shared_ptr<int> p( new int(42) );

process( shared_ptr<int>(p) );

此呼叫是正確的。

  引數位置的shared_ptr<int>(p),會建立一個臨時的shared_ptr賦予process的引數ptr,引用計數值加1,當呼叫所在的表示式結束時,這個臨時變數就會被銷燬,引用計數減1。同時將值拷貝給ptr,計數值加1。但函式結束後ptr被銷燬,引用計數減1。

最終p的引用計數還是1。記憶體不會被釋放。

練習12.11:如果我們像下面這樣呼叫process,會發生什麼?

process( shared_ptr<int>( p.get() ) );

這是一個嚴重的錯誤。

引數部分的 shared_ptr<int>( p.get() )會建立一個臨時的shared_ptr指向p.get()返回的內建指標。但是,這個臨時的shared_ptr和p是兩個獨立的shared_ptr,卻指向了同一片記憶體。當執行完畢後,ptr被銷燬,同時,它管理的記憶體也將被銷燬,此時p將管理一片被釋放了的記憶體。

練習12.12:p和q的定義如下,對於接下來的對process的每個呼叫,如果合法,解釋它做了什麼,如果不合法,解釋錯誤原因。

auto p = new int();

auto sp = make_shared_ptr<int>();

(a)process(sp);  //合法。

(b)process( new int() ); 

//合法。new建立了一個int物件,指向它的指標被用來建立了一個shared_ptr,傳遞給process的引數ptr,引用計數為1。當process執行完畢,ptr被銷燬,引用計數變為0,臨時int物件因而被銷燬。不存在記憶體洩漏和空懸指標的問題。

(c)process( p );  //不合法。不能將int*型別轉換為shared_ptr<int>。

(d)process( shared_ptr<int>( p ) ); //合法但是,process執行完畢後會使得p成為空懸指標。

練習12.13:如果執行下面的程式碼,會發生什麼?

auto sp = make_shared<int>();

auto p = sp.get();

delete p;

sp會成為一個類似空懸指標的shared_ptr。

練習12.14:編寫你自己版本的用shared_ptr管理connection的函式。

---------------------------------------------------------------------------------------------------------

插播一條PS:

如果你使用的智慧指標不是new分配的記憶體,記住傳遞它一個刪除器

---------------------------------------------------------------------------------------------------------

#include<iostream>
#include<memory>

using namespace std;

struct destination{ };//表示我們正在連線什麼
struct connection{ };//使用連線所需的資訊

connection connect( destination* );//開啟連線
void disconnect( connection );//關閉給定的連線

void f( destination& );
void f1( destination & );

void end_connection( connection *p );


int main()
{
    destination d;
    f1( d );
    f( d );

    return 0;
}

void end_connection( connection *p )
{
    disconnect( *p );
}


connection connect( destination *pd )
{
    cout << "開啟連線" << endl;
    return connection();
}
void disconnect( connection c )
{
    cout << "連線關閉" << endl;
}

void f( destination &d )
{
    //獲得一個連線
    connection c = connect( &d );
    cout << "用shared_ptr管理connect" << endl;
    shared_ptr<connection> sp( &c, end_connection );
    /* 獲得連線後的操作 */
    //忘記呼叫disconnect
    //當f退出時,不管是正常退出還是異常退出,connection都會被正確關閉。
}

void f1( destination &d )
{
    cout << "直接管理connect" << endl;
    connection c = connect( &d );
    //忘記呼叫disconnect
    cout << endl;
}


練習12.15:重寫第一題的程式,用lambda代替end_connection函式。
#include<iostream>
#include<memory>

using namespace std;

struct destination{ };//表示我們正在連線什麼
struct connection{ };//使用連線所需的資訊

connection connect( destination* );//開啟連線
void disconnect( connection );//關閉給定的連線

void f( destination& );
void f1( destination & );

//void end_connection( connection *p );


int main()
{
    destination d;
    f1( d );
    f( d );

    return 0;
}
/*
void end_connection( connection *p )
{
    disconnect( *p );
}

*/
connection connect( destination *pd )
{
    cout << "開啟連線" << endl;
    return connection();
}
void disconnect( connection c )
{
    cout << "連線關閉" << endl;
}

void f( destination &d )
{
    //獲得一個連線
    connection c = connect( &d );
    cout << "用shared_ptr管理connect" << endl;
    shared_ptr<connection> sp( &c, []( connection *p ){ disconnect( *p ); } );
    /* 獲得連線後的操作 */
    //忘記呼叫disconnect
    //當f退出時,不管是正常退出還是異常退出,connection都會被正確關閉。
}

void f1( destination &d )
{
    cout << "直接管理connect" << endl;
    connection c = connect( &d );
    //忘記呼叫disconnect
    cout << endl;
}
-------------------------------------------------------------------------------------------------------------------------------------------------------

unique_ptr不支援普通的拷貝和賦值!(原因是unique_ptr“擁有”它指向的物件)

但我們可以拷貝或賦值一個即將銷燬的unique_ptr。例如:返回一個unique_ptr。(編譯器將執行一種“特殊”的拷貝

--------------------------------------------------------------------------------------------------------------------------------------------------------

練習12.16:如果你試圖拷貝或賦值unique_ptr,編譯器並不總是能給出易於理解的錯誤資訊。編寫包含這種錯誤的程式,觀察編譯器是如何診斷這種錯誤的。

先略了。

練習12.17:下面的unique_ptr宣告中,哪些是合法的,哪些可能導致後續的程式錯誤?解釋每個錯誤的問題在哪裡。

int ix = 1024, *pi = &ix, *pi2 = new int(2048);

typedef unique_ptr<int> IntP;

(a) IntP p0(ix);

不合法。當我們定義一個unique_ptr時,需要將其繫結到一個new返回的指標上。

(b)IntP p1(pi);

不合法。不是new返回的指標。

合法。但是邏輯上是錯誤的。它用一個普通的int變數的地址初始化p1,p1銷燬時會釋放此記憶體,其行為是未定義的。

(c)IntP p2(pi2);

合法。

(d)IntP p3( &ix );

合法。但是同以上(b)的問題。

(e)IntP p4( new int(2048) );

合法。

(f)IntP p5( p2.get() );

合法。但是有問題。會造成兩個unique_ptr指向相同的記憶體地址。當其中一個unique_ptr被銷燬時(或者被reset釋放物件時),該記憶體被釋放,另一個unique_ptr會變成空懸指標。

練習12.18:shared_ptr為什麼沒有release成員。

unique_ptr不能賦值和拷貝,而release成員是用來轉移物件的所有權的。

 shared_ptr可以通過簡單的賦值和拷貝轉移實現共享所有權。

練習12.19:定義你自己版本的StrBlobPtr,更新StrBlob類,加入恰當的friend宣告及begin和end成員。

#ifndef STRBLOB_H_INCLUDED
#define STRBLOB_H_INCLUDED
#include<vector>
#include<string>
#include<stdexcept>
#include<initializer_list>
#include<memory>

using namespace std;

class StrBlobPtr;//友元類外部宣告。

class StrBlob{
public:
    //友元類宣告
    friend class StrBlobPtr;
    //建構函式
    StrBlob();
    StrBlob( initializer_list<string> il );
    //容器大小訪問
    vector<string>::size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    //新增和刪除元素
    void push_back( const std::string &str ) { data->push_back( str ); }
    void pop_back();
    //元素訪問
    string& front();
    const string& front() const;
    string& back();
    const string& back() const;
    //返回指向首元素和尾元素的StrBlobPtr
    StrBlobPtr begin();
    StrBlobPtr end();

private:
    std::shared_ptr<std::vector<std::string>> data;
    void check( vector<string>::size_type i, const std::string &msg ) const ;

};


inline StrBlob::StrBlob(): data( make_shared<vector<string>>() ) { }
inline StrBlob::StrBlob(initializer_list<string> il): data( make_shared<vector<string>> (il) ) { }


inline void StrBlob::check( vector<string>::size_type i, const std::string &msg ) const
{
    if( i >= data->size() )
        throw out_of_range( msg );
}

inline void StrBlob::pop_back()
{
    check( 0, "pop_back on empty StrBlob" );
    data->pop_back();
}

inline string& StrBlob::front()
{
    check( 0, "front on empty StrBlob" );
    return data->front();
}

inline const string& StrBlob::front() const
{
    check( 0, "front on empty StrBlob");
    return data->front();
}

inline string& StrBlob::back()
{
    check( 0, "back on empty StrBlob" );
    return data->back();
}

inline const string& StrBlob::back() const
{
    check( 0, "back on empty StrBlob" );
    return data->back();
}



//StrBlobPtr類

class StrBlobPtr{
    friend bool eq( const StrBlobPtr&, const StrBlobPtr& );
public:
    StrBlobPtr():curr(0) { }
    StrBlobPtr( StrBlob &a, size_t sz = 0 ): wptr( a.data ), curr( sz ) { }

    string& deref() const;//解引用
    StrBlobPtr& Incr();//字首遞增

private:
    shared_ptr<vector<string>> check( size_t, const string& ) const;
    weak_ptr<vector<string>> wptr;
    size_t curr;
};

inline shared_ptr<vector<string>> StrBlobPtr::check( size_t i, const string &msg ) const
{
    auto ret = wptr.lock();
    if( !ret )
        throw runtime_error( "unbound StrBlobPtr" );
    if( i >= ret->size() )
        throw out_of_range( msg );
    return ret;
}

inline string& StrBlobPtr::deref() const
{
    auto sp = check( curr, "dereference pass end" );

    return (*sp)[ curr ];
}

inline StrBlobPtr& StrBlobPtr::Incr()
{
    auto sp = check( curr, "increment past end of StrBlobPtr" );

    ++curr;

    return *this;
}


inline StrBlobPtr StrBlob::begin()
{
    return StrBlobPtr( *this );
}

inline StrBlobPtr StrBlob::end()
{
    auto ret = StrBlobPtr( *this, data->size() );
    return ret;
}


inline bool eq( const StrBlobPtr &lhs, const StrBlobPtr &rhs )
{
    auto l = lhs.wptr.lock(), r = rhs.wptr.lock();
    if( l == r )
        return ( !r || lhs.curr == rhs.curr );
    else
        return false;
}

inline bool uneq( const StrBlobPtr &lhs, const StrBlobPtr &rhs )
{
    return !eq( lhs, rhs );
}

#endif // STRBLOB_H_INCLUDED


練習12.20:編寫程式,逐行讀入一個輸入檔案,將內容存入一個StrBlob中,用一個StrBolbPtr打印出StrBlob中的每個元素。

見上題。

練習12.21:也可以這樣編寫StrBlobPtr的redef成員:

std::string& deref() const

{ return ( *check( curr, "deference past end"))[curr]; }

你認為哪個版本更好?為什麼?

第一個版本比較好,可讀性更好。也更容易修改。

練習12.22:為了能讓StrBlobPtr使用const StrBlob,你覺得應該如何修改?定義一個名為ConstStrBlobPtr的類,使其能夠指向const StrBlob。

為StrBlob的建構函式定義能夠接受const StrBlob&引數的版本。

然後為StrBlob定義const版本的begin和end成員。

練習12.23:編寫一個程式,連線兩個字串字面常量,將結果儲存在一個動態分配的char陣列中。重寫這段程式,連線兩個標準庫string物件。

c_str() 返回string物件的記憶體地址。

#include<iostream>
#include<string>
#include<cstring>

using namespace std;

int main()
{
    const char *s1 = "Hello ";
    const char *s2 = "world";

    char *r = new char[ strlen(s1) + strlen(s2) + 1 ];
    strcpy( r, s1 );
    strcat( r, s2 );
    cout << r << endl;

    string str1 = "Fuck U ";
    string str2 = "Bitch";
    strcpy( r, (str1 + str2).c_str() );
    cout << r << endl;
    delete []r;
    return 0;
}

練習12.24:編寫一個程式,從標準輸入讀取一個字串,存入一個動態分配的字元陣列中。描述你的程式如何處理變長輸入。測試你的程式,輸入一個超出你分配的陣列長度的字串。

處理變長輸入的方法是:根據動態分配的字元陣列的大小確定字串長度的閾值,當讀取的字元數超過閾值時,停止讀取,也就是採取了截斷的方式。

程式略了。

練習12.25:給定下面的new表示式,你應該如何釋放pa?

int *pa = new int[10];

delete [] pa;

練習12.26:用allocator重寫427頁的程式。

#include<iostream>
#include<memory>

using namespace std;

int main()
{
    allocator<string> alloc;
    auto const p = alloc.allocate( 100 );
    string word;
    string *q = p;
    while( cin >> word && q != p + 100 )
        alloc.construct( q++, word );
    auto sz = q - p;
    cout << "alloc的資料是:" << endl;
    for( size_t i = 0; i != sz; ++i )
        cout << p[i] << " ";
    cout << endl;

    while( q != p )
        alloc.destroy( --q );

    alloc.deallocate( p, 100 );

    return 0;
}


練習12.27:TextQuery和QueryResult類只使用了我們已經介紹過的語言和標準庫特性。不要提前看後續章節內容,只用已經學到的知識對這兩個類編寫你自己的版本。

值得一提的是:這個檢索工具對標點符號敏感:也就是 "me," 和 "me"不是對應同一個關鍵字。

類的定義。

#ifndef TEXTQUERY_H_INCLUDED
#define TEXTQUERY_H_INCLUDED

#include<vector>
#include<fstream>
#include<memory>
#include<sstream>
#include<string>
#include<map>
#include<set>

using namespace std;

class QueryResult; //前向宣告

class TextQuery{
public:
    //類型別名
    typedef vector<string>::size_type line_no;
    //建構函式。
    TextQuery( ifstream& );
    QueryResult query( const string& ) const;
private:
    shared_ptr<vector<string>> file;
    map<string, shared_ptr<set<line_no>>> wordMap;

};

//建構函式定義。讀取檔案按行存入vector,並且建立單詞與行號對映。
TextQuery::TextQuery( ifstream &fin ): file( new vector<string> )
{
    string line;
    //為了使得行號從1開始對應。我把vector的位置0先設為空串。
    file->push_back( "" );
    while( getline( fin, line ) ){
        file->push_back( line );
        unsigned lineN = file->size() - 1; //lineN用來儲存行號。
        istringstream sin( line );
        string word;
        while( sin >> word ){
            auto &lines = wordMap[ word ]; //lines是一個shared_ptr
            if( !lines )
                lines.reset(  new set<line_no> ); //如果lines為空指標(set沒有元素),分配一個新的set
            lines->insert( lineN );
        }
    }
}


class QueryResult{
    friend ostream& print( ostream&, const QueryResult& );
public:
    QueryResult( const string& str, shared_ptr<set<TextQuery::line_no>> l,
                shared_ptr<vector<string>> f ): sought( str ), lines( l ), file( f ) { }
private:
    string sought;
    shared_ptr<set<TextQuery::line_no>> lines;
    shared_ptr<vector<string>> file;

};

QueryResult TextQuery::query( const string &s ) const
{
    static shared_ptr<set<line_no>> nodata( new set<line_no> );

    auto loc = wordMap.find( s );
    if( loc == wordMap.end() )
        return QueryResult( s, nodata, file );
    else
        return QueryResult( s, loc->second, file );
}

ostream& print( ostream &os, const QueryResult &qr )
{
    os << qr.sought << " occurs " << qr.lines->size()
        << ( ( qr.lines->size() > 1 ) ? " times" : " time" )
        << endl;
    for( auto num : *( qr.lines ) )
        os << "\t(line " << num << ") " <<  *( qr.file->begin() + num ) << endl;
    return os;
}

#endif // TEXTQUERY_H_INCLUDED

測試程式。
#include<iostream>
#include"TextQuery.h"
#include<fstream>

using namespace std;

int main()
{
    ifstream fin;
    fin.open( "T11_9_data.txt" );
    if( !fin ){
        cerr << "cant open file!";
        return -1;
    }
    TextQuery tq( fin );
    print( cout, tq.query( "me" ) );

    return 0;
}


練習12.28:編寫程式實現文字查詢,不要定義類來管理資料。你的程式應該接受一個檔案,並與使用者互動來查詢單詞。使用vector、map和set容器來儲存來自檔案的資料並生成查詢結果。

#include<iostream>
#include<fstream>
#include<vector>
#include<sstream>
#include<map>
#include<set>
#include<cctype>

using namespace std;

typedef vector<string>::size_type line_no;
//使用全域性變數。省卻引數傳遞。
vector<string> file;
map<string, set<line_no>> wordMap;

string cleanup_str( const string &str );
void input_text( ifstream &fin );
ostream& query_and_print( ostream &os, const string &sought );
void runRequiries( ifstream &fin );

int main()
{
    ifstream fin;
    fin.open( "T11_9_data.txt" );
    if( !fin ){
        cerr << "cant open file!";
        return -1;
    }
    runRequiries( fin );
    fin.close();

    return 0;
}

//改善程式,忽略大小寫,忽略標點符號
string cleanup_str( const string &str )
{
    string result; //空串,儲存轉換後的字串
    for( const auto &c : str )
        if( !ispunct( c ) )
            result += tolower( c );

    return result;
}

//把文字按行儲存在vector 和 建立每個單詞的對映
void input_text( ifstream &fin )
{
    string line;
    while( getline( fin, line ) ){
        file.push_back( line );
        auto lineN = file.size() - 1;
        istringstream sin( line );
        string word;
        while( sin >> word )
            wordMap[ cleanup_str( word ) ].insert( lineN );
    }
}

ostream& query_and_print( ostream &os, const string &sought )
{
    auto it = wordMap.find( sought );
    if( it == wordMap.end() ){
        os << "cant find " << sought << endl;
        return os;
    }

    os << sought << " occurs " << (it->second).size()
        << ( ( (it->second).size() > 1 )? "times":"time" ) << endl;

        for( const auto &index : it->second )
            os << "\t(line " << ( index + 1 ) << ") "
                << file[ index ] << endl;
        os << endl;

        return os;
}

void runRequiries( ifstream &fin )
{
    input_text( fin );
    string word;
    cout << "請輸入你要查詢的單詞:( q to quit )"  << endl;
    while( 1 ){
        if( !(cin >> word ) || word == "q" )
            break;
        query_and_print( cout, word );
    }
    cout << "程式結束,拜拜!" << endl;
}


練習12.29:我們曾經用do while迴圈來編寫管理使用者互動的迴圈。用do while迴圈重寫本節程式,解釋你傾向於哪個版本,為什麼?

兩者差異不大。略了。

練習12.30:定義你自己版本的TextQuery和QueryResult類,並執行12.3.1節中的runRequery函式。

見練習12.27.(12.27的版本不是使用者輸入查詢的,是程式設計師自己驗證一種情況的版本)

練習12.31:如果用vector代替set儲存行號,會有什麼差別?哪種方法更好?為什麼?

vector儲存行號:

因為插入行號的過程中,是按行號順序插入的,所以行號的排序並不是問題。

但是vector儲存行號的話會儲存重複插入相同的行號(但是可以通過簡單操作來避免)。

而vector插入元素的時間複雜度是常數時間,set是紅黑樹實現的,插入操作時間複雜度是O(logn)。vector的插入效能更好。

練習12.32:重寫TextQuery和QueryResult類,用StrBlob代替vector<string>儲存輸入檔案。

先略了。(標記一下,之後回來做)

練習12.33:在第15章中我們將擴充套件查詢系統,在QueryResult類中將會需要一些額外的成員。新增名為begin和end的成員,返回一個迭代器,指向一個給定查詢返回的行號的set中的位置。在新增一個名為get_file的成員,返回一個shared_ptr,指向QueryResult物件中的檔案。

先擱置。