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

C++Primer習題第七章

練習7.1:使用2.6.1節練習定義的Sales_data類為1.6節的交易處理程式編寫一個新的版本。

#include<iostream>
#include"Sales_data.h"

using namespace std;

int main()
{
    Sales_data total;
    cout << "請輸入交易記錄(ISBN、銷售量、原價、實際售價):" << endl;

    if( cin >> total )
    {
        Sales_data trans;
        while( cin >> trans )
        {
            if( compareIsbn( total, trans ) )
                total += trans;
            else
            {
                cout << total << endl;
                total = trans;
            }
        }
        cout << total << endl;
    }
    else
    {
        cerr << "沒有資料!" << endl;
        return -1;
    }

    return 0;
}

練習7.2:曾在2.6.2節的練習編寫了一個Sales_data類,請向這個類新增combine和isbn成員。
class Sales_data
{
public:
    string isbn() const { return bookNo; }
    Sale_data& combine( const Sales_data &rhs )
    {
        units_sold += rhs.units_sold;
        sale_price = ( units_sold * sale_price + rhs.units_sold * rhs.sale_price )
                     / ( units_sold + rhs.units_sold );
        if( selling_price != 0 )
            discount = sale_price / selling_price;
        
        return *this;
    }
        
private:
    string bookNo;
    unsigned units_sold = 0;
    double selling_price = 0.0;
    double sale_price = 0.0;
    double dicount = 0.0;
}



練習7.3:修改7.1.1節的交易處理程式,令其使用這些成員。

class Sales_data
{
public:
    string isbn() const { return bookNo; }
    Sale_data& combine( const Sales_data &rhs )
    {
        units_sold += rhs.units_sold;
        sale_price = ( units_sold * sale_price + rhs.units_sold * rhs.sale_price )
                     / ( units_sold + rhs.units_sold );
        if( selling_price != 0 )
            discount = sale_price / selling_price;
        
        return *this;
    }
        
private:
    string bookNo;
    unsigned units_sold = 0;
    double selling_price = 0.0;
    double sale_price = 0.0;
    double dicount = 0.0;
}


練習7.4:編寫一個名為Person的類,使其表示人員的姓名和住址。使用string物件存放這些元素,接下來的練習將不斷充實這個類的其他特徵。
class Person
{
private:
    string Name;
    string Address;
public:
    string getName() const { return Name; }
    string getAddress() const { return Address; }
};

練習7.5:在你的Person類中提供一些操作使其能夠返回姓名和住址。這些函式是否應該是const的呢?解釋原因。

類的定義如上,已經包含getName()和getAddress()成員。

這些函式應該是const的,因為,這兩個成員函式的操作只是讀取資料成員的值,並不需要改動它們的值。

練習7.6:對於函式add、read和print,定義你自己的版本。

類的作者常常需要定義一些輔助函式。雖然這些屬於類的介面的組成部分,但它們實際上並不屬於類本身

Sales_data& add( Sales_data &rhs, Sales_data &lhs );
std::istream& read( std::istream& in, Sales_data item );
ostream& print( ostream& out, const Sales_data );

Sales_data add( Sales_data &rhs, Sales_data &lhs )
{
    Sales_data sum = rhs;
    sum.combine( lhs );
    return sum;
}

std::istream &read( std::istream& in, Sales_data item )
{
    in >> item.bookNo >> item.units_sold >> item.selling_price >> item.sale_price;
    return in;
}

std::ostream &print( std::ostream &out, const Sales_data &rhs )
{
    out << rhs.bookNo << " " << rhs.units_sold << " " << rhs.selling_price << " "
        << rhs.sale_price << " " << discount;
    return out;
}





練習7.7:使用這些新函式重寫7.1.2節練習中的交易處理程式。

這裡需要注意,add、read、print函式被需要宣告為該類的友元函式,否則無法訪問該類的private資料成員。

#include<iostream>
#include"Sales_data.h"

using namespace std;

int main()
{
    cout << "請輸入交易記錄(ISBN、銷售量、原價、實際售價):" << endl;
    Sales_data total;
    if( read( cin, total ) ){
        Sales_data trans;
        while( read( cin, trans ) ){
            if( total.isbn() == trans.isbn() )
                total = add( total, trans );
            else{
                print( cout, total );
                cout << endl;
                total = trans;
            }

        }
        print( cout, total );
        cout << endl;
    }
    else{
    cerr << "No data?" << endl;
    return -1;
    }
    return 0;
}

練習7.8:為什麼read函式將其Sales_data引數定義成普通的引用,而print將其引數定義成常量引用?

因為read函式從輸入流中讀取資料並寫入到Sales_data類物件,因此需要有修改物件的許可權。

print函式只負責對類物件的資料的輸出,不需要對其作出任何更改。

練習7.9:對於7.1.2節練習中的程式碼,新增讀取和列印Person物件的操作。

class Person
{
    friend std::istream &read( istream&, Person& );
    friend std::ostream &print( ostream&, const Person&);
private:
    string Name;
    string Address;
public:
    string getName() const { return Name; }
    string getAddress() const { return Address; }
};

inline std::istream &read( istream &is, Person &aPerson)
{
    is >> aPerson.Name >> aPerson.Address;
    return is;
}

inline std::ostream &print( ostream &os, const Person &aPerson )
{
    os << aPerson.getName() << aPerson.getAddress();
    return os;
}
練習7.10:在下面這條if語句中,條件部分的作用是什麼?

    if( read( read( cin, data1 ), data2 ) )

read函式返回型別是std::istream&,輸入流的引用,所以可以作為實參繼續使用和作為條件判斷。

如果cin的資料正確寫入data1和data2,則條件為真。否則為假。

練習7.11:在你的Sales_data類中新增建構函式,然後編寫一段程式令其用到每個建構函式。

#include<iostream>
#include"Sales_data.h"
using namespace std;

int main()
{
    //使用四種建構函式
    Sales_data dat1;
    Sales_data dat2( "劍指offer" );
    Sales_data dat3( "程式設計之美", 20, 45, 37 );
    Sales_data dat4( cin );
    cout << "書籍的銷售情況是:" << endl;
    cout << dat1 << endl;
    cout << dat2 << endl;
    cout << dat3 << endl;
    cout << dat4 << endl;

    return 0;
}


練習7.12:把只接受一個istream作為引數的建構函式定義到類的內部。

class Sales_data
{
public:
    string isbn() const { return bookNo; }
    Sale_data& combine( const Sales_data &rhs );

public:
    Sales_data() = default;
    Sales_data( const std::string &book ): bookNo(book) {}
    Sales_data( const std::string &book, const unsigned units, const double sellingprice, const double saleprice );
    Sale_data( istream& ) { is >> *this; }
private:
    string bookNo;
    unsigned units_sold = 0;
    double selling_price = 0.0;
    double sale_price = 0.0;
    double dicount = 0.0;
}

練習7.13:使用istream建構函式重寫第229頁的程式。

#include<iostream>
#include"Sales_data.h"


using namespace std;


int main()
{
    cout << "請輸入交易記錄:(ISBN、銷量、原價、實際售價):" << endl;
    Sales_data total( cin );
    if( cin )
    {
        Sales_data trans( cin );
        do
        {
            if( total.isbn() == trans.isbn() )
                total.combine( trans );
            else
            {
                print( cout, total );
                cout << endl;
                total = trans;
            }
        }while( read( cin, trans ) );
        print( cout, total);
        cout << endl;
    }
    else
    {
        cerr << " No data?! " << endl;
        return -1;
    }

    return 0;
}


練習7.14:編寫一個建構函式,令其用我們提供的類內初始值顯示地初始化成員。

Sales_data( const string &s ) : bookNo(s), units_sold(0), selling_price(0), sale_price(0), discount(0)  { }

練習7.15:為你的Person類新增正確的建構函式。

class Person
{
    friend std::istream &read( istream&, Person& );
    friend std::ostream &print( ostream&, const Person&);
private:
    string Name;
    string Address;
public:
    Person() = default;
    person( const string &Nam, const string &Adr ): Name( Nam ), Address( Adr ) { }
    person( istream &is ) { read( is, *this ) }

public:
    string getName() const { return Name; }
    string getAddress() const { return Address; }
};

inline std::istream &read( istream &is, Person &aPerson)
{
    is >> aPerson.Name >> aPerson.Address;
    return is;
}

inline std::ostream &print( ostream &os, const Person &aPerson )
{
    os << aPerson.getName() << aPerson.getAddress();
    return os;
}

練習7.16:在類的定義中對於訪問說明符出現的位置和次數有限定嗎?如果有,是什麼?什麼樣的成員應該定義在public說明符之後?什麼樣的成員應該定義在private說明符之後?

定義在public之後的成員在整個程式內可被訪問,public成員定義類的介面

定義在private之後的成員可以被類的成員函式訪問,但是不能被使用該類的程式碼訪問。

對於某個訪問說明符能出現多少次沒有嚴格限定。

一般來說,作為介面的一部分,建構函式和一部分成員函式應該定義在public說明符之後,而資料成員和作為實現部分的函式則應該跟在private後面。

練習7.17:使用struct和class時有區別嗎?如果有,是什麼?

struct和class的預設訪問許可權不同。

struct預設成員是public的

class預設成員是private的。

練習7.18:封裝是何含義?它有什麼作用?

(標準)

答:

(1)封裝是指保護類的成員不被隨意訪問的能力。通過把類的實現細節設定為private,我們就能完成類的封裝。

封裝實現了介面和實現的分離。

(2)封裝有兩個重要的優點:一是確保使用者程式碼不會無意間破壞物件的狀態;二是被封裝的類的具體實現細節可以隨時改變,而無須調整使用者級別的程式碼。

只要類的介面不變,使用者程式碼就無須改變。

練習7.19:在你的Person類中, 你將把哪些成員宣告成public的?哪些宣告成private的?解釋這樣做的原因。

建構函式和介面( getName() 、 getAddress() )設定為public,以便於外部訪問。

資料成員Name和Address設定為private,防止使用者不經意修改和破壞它們。

練習7.20:友元在什麼時候有用?請分別列舉出使用友元的利弊。

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

插播一條PS:

友元宣告只能出現在類定義的內部,但是在類內出現的具體位置不限。其不受訪問控制級別的約束。(不是類內成員)

友元的宣告僅僅指定了其訪問的許可權,並非通常意義上的函式宣告。故友元宣告之外還需專門對函式進行一次宣告。

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

答:(標準)

當非成員函式確實需要訪問類內的私有成員時,我們可以把它宣告成該類的友元。

此時,友元可以“工作在類的內部”,像類成員一樣訪問類的所有資料和函式。但是一旦使用不慎,就有可能破壞類的封裝性。

練習7.21:修改你的Sales_data類使其隱藏實現的細節。你之前編寫的關於Sales_data操作的程式應該繼續使用,藉助類的新定義重新編譯該程式,確保其工作正常。

略了。(之前的程式已封裝好了)

練習7.22:修改你的Person類使其隱藏實現的細節。

class Person
{
    friend std::istream &read( istream&, Person& );
    friend std::ostream &print( ostream&, const Person&);
private:
    string Name;
    string Address;
public:
    Person() = default;
    person( const string &Nam, const string &Adr ): Name( Nam ), Address( Adr ) { }
    person( istream &is ) { read( is, *this ) }

public:
    string getName() const { return Name; }
    string getAddress() const { return Address; }
};

inline std::istream &read( istream &is, Person &aPerson)
{
    is >> aPerson.Name >> aPerson.Address;
    return is;
}

inline std::ostream &print( ostream &os, const Person &aPerson )
{
    os << aPerson.getName() << aPerson.getAddress();
    return os;
}


練習7.23:編寫你的screen類。

class Screen
{   
public:
    typedef std::string::size_type pos;

private:
    std::string contents;
    pos cursor = 0;
    pos height = 0;
    pos width = 0;
};


練習7.24:給你的Screen類新增三個建構函式:一個預設建構函式;另一個建構函式接受寬和高的值,然後將contents初始化成給定數量的空白;第三個建構函式接受寬和高的值以及一個字元,該字元作為初始化之後螢幕的內容。

class Screen
{   
public:
    typedef std::string::size_type pos;
//建構函式
    Screen() = default;
    Screen( const pos ht, const pos wt ) : height( ht ), width( wt ), contents( ht * wt, ' ' ) { }
    Screen( const pos ht, const pos wt, const char c ) : height( ht ), width( wt ), contents( ht * wt, c ) { }
     
private:
//資料成員
    std::string contents;
    pos cursor = 0;
    pos height = 0;
    pos width = 0;
};


練習7.25:Screen能安全地依賴於拷貝和賦值操作的預設版本嗎?如果能,為什麼?如果不能,為什麼?

能。因為Screen的資料成員都是內建型別(string類定義了拷貝和賦值運算子),因此直接使用類物件執行拷貝和賦值操作是可以的。

練習7.26:將Sales_data::avg_price定義成行內函數。

如果定義在類內部,自動隱式內聯。

如果定義在類外部,加上inline

練習7.27:給你自己的Screen類新增move、set和display函式,通過執行下面的程式碼檢驗你的類是否正確。

class Screen
{   
public:
    typedef std::string::size_type pos;
//建構函式
    Screen() = default;
    Screen( const pos ht, const pos wt ) : height( ht ), width( wt ), contents( ht * wt, ' ' ) { }
    Screen( const pos ht, const pos wt, const char c ) : height( ht ), width( wt ), contents( ht * wt, c ) { }
public:
//成員函式
    Screen& cursor_move( const pos r, const pos c )
    {
        cursor = r * width + c;
        return *this;
    }
    Screen& set_ch( const pos r, const pos c, const char ch )
    {
        contents[ r * width + c ] = ch;
        return *this;
    }
    Screen& set_ch( const char ch )
    {
        contents[ cursor ] = ch;
        return *this;
    } 
    Screen& display( void )
    {
        std::cout << contents;
        return *this;
    }
     
private:
//資料成員
    std::string contents;
    pos cursor = 0;
    pos height = 0;
    pos width = 0;
};

#include<iostream>
#include"Screen.h"

using namespace std;

int main()
{
    Screen myScreen( 5, 5, 'X' );
    myScreen.cursor_move( 4, 0 ).set_ch( '#' ).display();
    cout << "\n";
    myScreen.display();
    cout << "\n";

    return 0;
}

練習7.28:如果move 、set 、display函式的返回型別不是Screen&而是Screen,則在上一個練習中將會發生什麼情況?

返回引用的函式是左值的,意味著這些函式的返回的是物件本身而不是物件的副本。如果不是Screen,則上述函式各自只返回一個臨時副本,不會改變myScreen的值。

練習7.29:修改你的Screen類,令move、Screen和display函式返回Screen並檢查程式的執行結果,在上一個練習中的你的推測正確嗎?

正確。

myScreen物件的值並沒有被改變。

練習7.30:通過this指標使用成員的做法雖然合法,但是有點多餘。討論顯式使用指標訪成員的優缺點。

通過this指標訪問成員的優點:非常明確地指出訪問的是物件的成員,並且,可以在成員函式中使用與資料成員同名的形參;

缺點:顯得多餘,不簡潔。

練習7.31:定義一對類X和Y,其中X包含一個指向Y的指標,而Y包含一個型別為X的物件。

要建立一個類物件必須要在該類的定義之後。

class Y; //前向宣告

class X

{

Y*  y;//此時Y是一個不完全型別(未被定義)

};

class Y

{

X  x;  //在X的定義

};

練習7.32:定義你自己的Screen和Window_mgr,其中clear是Window_mgr的成員,是Screen的友元。

先空著...在vector<class_type>容器的型別使用自定義類型別出現了問題還沒能夠解決。

練習7.33:如果我們給Screen新增一個如下所示的size成員將發生什麼情況?如果出現了問題,請嘗試修改它。

修改後:

Screen:: pos Screen::size() const

{

return height * width;

}

練習7.34:如果我們把第256頁Screen類的pos的typedef放在類的最後一行會發生什麼情況?

編譯出現錯誤。因為這樣會造成對pos的使用在pos的宣告之前,此時的編譯器不知道pos是什麼含義。

練習7.35:解釋下面程式碼的含義,說明其中的Type和initVal分別使用了哪個定義。如果程式碼存在錯誤,嘗試修改它。

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

插播一條PS:

在類中,如果成員使用了外層作用域某個名字,而該名字代表一種型別,則類不能在之後重新定義該名字。

所以,如果成員沒有使用外層作用域的型別的話,類還是能夠重新定義同名字的型別的別名的。

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

typedef string Type;//外層作用域,Type是string的類型別名

Type initVal(); //宣告initVal函式,該函式的返回型別是外層的Type,也就是string

class Exercise{

public:

typedef double Type; //內層作用域。Type是double的類型別名

Type setVal( Type );  //宣告函式setVal, 形參型別和返回型別都是內層的Type,也就是double

Type initVal(); //重新宣告initVal函式,返回型別是內層的Type

private:

int val;

};

Type Exercise::setVal( Type parm ) {    //第一個Type是類外的,是string。 第二個Type是類內的,是double

val = parm + initVal();  //此處的initVal是類內的

return val;

}

以上會發生編譯錯誤。因為setVal成員函式的return語句實際返回型別和返回型別不匹配。

所以setVal函式應該修改為:

Exercise::Type Exercise::setVal( Type parm ) { 

val = parm + initVal();  

return val;

}


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

獨立的ps:

如果沒有在建構函式的初始值列表中顯示地初始化成員,則該成員將在構造函式體之前執行預設初始化

要養成使用建構函式初始值的習慣。

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

練習7.36:下面的初始值是錯誤的,請找出問題所在並嘗試修改它。

struct X{

X( int i, int j ) : base( i ), rem( base % j ) { }

int rem, base;

};

因為X先定義rem再定義base,所以X建構函式先初始化的是rem,而rem( base % j ) 裡的base此時是未定義的。

修改:

struct X{

X( int i, int j ) : base( i ), rem( i % j ) { }

int rem, base;

};

練習7.37:使用本節提供的Sales_data類,確定初始化下面的變數時分別使用了哪個建構函式,然後羅列出每個物件所有資料成員的值。

略了。

練習7.38:有些情況下我們希望提供cin作為接受istream&引數的建構函式的預設實參,請宣告這樣的建構函式。

class Sales_data

{

public:

Sales_data( std::istream &is = std::cin ) { is >> *this; }

//其他的一致。

}


練習7.39:如果接受string的建構函式和接受istream&的建構函式都是用預設實參,這種行為合法嗎?如果不,為什麼?

答:

不合法。

如果不提供實參的情況下,會產生二義性錯誤,編譯器無法判斷呼叫這兩個建構函式的哪一個。

練習7.40:從下面的抽象概念中選擇一個(或者你自己指定一個),思考這樣的類需要哪些資料成員,提供一組合理的建構函式並闡明這樣做的原因。

(a)book

class book{

private:

string bookName;

string Author;

string ISBN;

string Publisher;

double price = 0.0;

public:

book() = default;

book( std::istream &is ) { is >> *this; }

book( const string &n, const string &a, const string &I, const string &P, double pr );

};

練習7.41:使用委託建構函式重新編寫你的Sales_data類,給每個建構函式新增一條語句,令其一旦執行就列印一條資訊。用各種可能的方式分別建立Sales_data物件,認真研究每次輸出的資訊直到你確實理解了委託函式的執行順序。

略了。

練習7.42:對於你在練習7.40中編寫的類,確定哪些建構函式可以使用委託。如果可以的話,編寫委託建構函式。如果不可以,從抽象概念列表中重新選擇一個你認為可以使用委託建構函式的,為挑選出的這個概念編寫類定義。

class book{
private:
	string bookName;
	string Author;
	string ISBN;
	string Publisher;
	double price = 0.0;
public:
	book( const string &n, const string &a, const string &I, const string &P, double pr ) :
	    bookName( n ), Author( a ), ISBN( I ), Publisher( P ), price( pr ) { }
    book() : book( "", "", "", "", 0 ) { }
    book( std::istream &is ) : book() { is >> *this; }
};

練習7.43:假定有一個名為NoDefault的類,它有接受一個int的建構函式,但是沒有預設建構函式。定義類C,C有一個NoDefault型別的成員,定義C的預設建構函式。

#include<iostream>

using namespace std;

class NoDefault{  //NoDefault沒有預設建構函式,編譯器也不會為其合成一個。
public:
    NoDefault( int i ) : val( i ) { }
private:
    int val;
};

class C{
public:
    C( int m = 0 ) : men( m ) { }

private:
    NoDefault mem;
};


練習7.44:下面這條宣告合法嗎?如果不,為什麼?

vector<NoDefault> vec( 10 );

不合法。NoDefault類型別沒有預設建構函式,不能預設初始化。編譯器報錯。

練習7.45:如果在上一個練習中定義的vector元素型別是C,該宣告合法嗎?為什麼?

合法。C定義了帶引數的預設建構函式,可以執行預設初始化。

練習7.46:下面的哪些論斷是不正確的?為什麼?

(a)一個類必須至少提供一個建構函式。

不正確。因為沒有提供建構函式,編譯器會合成一個預設建構函式。

(b)預設建構函式是引數列表為空的建構函式。

不正確。預設建構函式也可以帶引數,但須提供預設實參。

(c)如果對於類來說不存在有意義的預設值,則類不應該提供預設建構函式。

不正確。因為如果一個類沒有預設建構函式,則當編譯器確實需要隱式地使用預設建構函式時,該類無法使用。所以一般情況下,都應該為類構建一個預設建構函式。

(d)如果類沒有定義預設建構函式,則編譯器將為其生成一個並把每個資料成員初始化成相應型別的預設值。

不正確。對於編譯器合成的預設建構函式來說,類型別的成員執行了各自所屬類的預設建構函式,內建型別和複合型別的成員只對定義在全域性作用域的物件執行初始化。

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

插播一條PS:

如果建構函式接受一個實參,則它實際上定義了轉換此類型別的隱式轉換機制,這種建構函式稱作轉換建構函式。建構函式的引數型別類型別隱式轉換。

explicit抑制這種隱式轉換。即使explicit聲明瞭,但是如果希望能轉換,可以顯式地用類建構函式或者使用static_cast強制轉換。explicit只能在類內的建構函式宣告。

explicit建構函式接初始化

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

練習7.47:說明接受一個string引數的Sales_data建構函式是否應該是explicit的,並解釋這樣做的優缺點。

答:應該是explicit的。否則,編譯器有可能自動把一個string物件轉換成Sales_data物件,這種做法有時候會與初衷相違背。

這樣做的優點:避免了因隱式類型別轉換而帶來意想不到的錯誤。缺點是,當用戶的確需要這樣的類型別時,不得不使用略顯繁瑣的方式來實現。

練習7.48:假定Sales_data的建構函式不是explicit的,則下述定義將執行什麼樣的操作?如果Sales_data的建構函式是explicit的,又將發生什麼呢?

string null_isbn( "9-999-9999-9" ); //將string物件null_isbn初始化成"9-999-9999-9"

Sales_data item1( null_isbn );  //呼叫接受一個string的Sales_data的建構函式通過null_isbn構造item1物件。

Sales_data item2( "9-999-9999-9");//同上。無須型別轉換

如果Sales_data的建構函式是explicit的,item1和item2依然都能被正確地建立。

練習7.49:對於combine函式的三種不同的宣告,當我們呼叫i.combine(s)時分別發生什麼情況?其中i是一個Sales_data,而s是一個string物件。

(a)Sales_data &combine( Sales_data );

s隱式地呼叫Sales_data的建構函式,生成一個臨時的Sales_data物件,然後傳遞給combine的形參。

(b)Sales_data &combine( Sales_data& );

編譯無法通過。因為combine成員函式的形參是非常量引用,但是s自動建立的Sales_data臨時物件無法傳遞給combine所需的非常量引用。(PS:隱式轉換生成的無名的臨時物件是const的

修改為:Sales_data &combine( const Sales_data& ) 就可以了。

(c)Sales_data &combine( const Sale_data& ) const;

編譯無法通過。因為我們把combine成員函式宣告成了常量成員函式,所以該函式無法修改資料成員的值。

練習7.50:確定在你的Person類中是否有一些建構函式應該是explicit的。

Sales_data( std::istream &is ) { is >> *this; }

這個建構函式應該是explicit的。我們更傾向於嚴格控制Person物件的生成過程,如果確實需要使用Person物件,可以明確指定;其他情況下則不希望自動型別轉換的發生。所以應該把這個建構函式宣告成explicit的。

練習7.51:vector將其單引數的建構函式定義成explicit的,而string則不是,你覺得原因何在?

string接受的單引數型別是const char*型別,如果我們得到了一個常量字串指標,則把它看做string物件是自然而然的過程。無須指定為explicit的。

但是vector接受的單引數型別是int型別,指定vector的容量。如果在本來需要vector的地方提供一個int值並且希望這個int值自動轉換成vector,這個過程比較牽強。

練習7.52:使用2.6.1節的Sales_data類,解釋下面的初始化過程。如果存在問題,嘗試修改它。

Sales_data item = { "978-0590353403", 25, 15.99 };

原意是執行聚合類的初始化。

但是Sales_data的資料成員含有類內初始值,不滿足條件。

修改為:

struct Sales_data{

string bookNo;

unsigned units_sold;

double revenue;

};

練習7.53:定義你自己的Debug。

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

字面值常量類:

資料成員都是字面值型別聚合類。注意是字面值型別,我一開始看成了字面值常量。算術型別、引用和指標都屬於字面值型別。某些類也是字面值型別,它們可能含有constexpr函式成員

若不是聚合類,但符合某些要求,則它也是一個字面值常量。

constexpr建構函式用於生成constexpr物件以及constexpr函式的引數或返回型別

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

練習7.54:Debug中以set_開頭的成員應該被宣告成constexpr嗎?如果不,為什麼?

不應該。因為constexpr,這些函式的作用是設定資料成員的值,而constexpr函式只能包含return語句,不允許執行其他任務。

練習7.55::7.5.5節的Data類是字面值常量類嗎?請解釋原因。

是字面值常量類。因為Data類是聚合類,然後他的資料成員也全是字面值型別的。

練習7.56:什麼是類的靜態成員?它有何優點?靜態成員與普通成員有何區別?

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

插播一條PS:

即使一個常量靜態資料成員在類內部被初始化了,通常情況下也應該在類的外部定義一下該成員。

靜態資料成員可以是不完全型別。特別的:靜態資料成員可以就是它所屬的類型別,而非靜態資料成員則受到限制,只能宣告成它所屬類的指標或引用。

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

答:

靜態成員是指宣告語句之前帶有關鍵字static的類成員,靜態成員不是任意單獨物件的組成部分,而是由該類的全體對 象所共享。

靜態成員的優點包括:作用域位於類的範圍之內,避免與其他類的成員或者全域性作用域的名字衝突;可以是私有成員,而全域性物件不可以;通過閱讀程式可以非常容易地看出靜態成員與特定類關聯,使得程式的含義清晰明瞭。

靜態成員與普通成員的區別主要體現在普通成員與類的物件關聯,是某個具體物件的組成部分;而靜態成員不從屬於任何具體的物件,它由該類的所有物件共享。另外,還有一個細微的區別,靜態成員可以作為預設引數,而普通成員不能作為預設引數。

練習7.57:編寫你的Account類。

class Account{
public:
    static double rate() { return interestRate; }
private:
    std::string Name;
    double Amount = 0.0;
    static double interestRate;
};

練習7.58:下面的靜態資料成員的宣告和定義有錯誤嗎?請解釋原因。

除了靜態常量成員之外,其他靜態成員不能在類的內部初始化。  

所以rate和vec不能在類內初始化。

所以rate和vec的類外定義必須給出其初始值。

相關推薦

C++Primer習題

練習7.1:使用2.6.1節練習定義的Sales_data類為1.6節的交易處理程式編寫一個新的版本。 #include<iostream> #include"Sales_data.h" using namespace std; int main() {

C++ primer 習題(2)

4.26 編寫程式從標準輸入裝置讀入一個string型別的字串。考慮如何程式設計實現從標準輸入裝置讀入一個C風格字串。 int main() { cout << "C++ style" << endl; string str; cin >> str; c

C++primer 習題(1)

4.7編寫程式碼實現一個數組賦值給另外一個數組,然後將這段程式碼改用vector實現。考慮如何將一個vector 賦值給另一個vector。 int main() { int a[3] = { 1,2,3 }; int b[3]; cout << "array :" <&l

C Primer Plus 課後答案

目錄 複習題 a.5 > 2 q c h b 程式設計練習

The C Primer Plus 答案

7.2 #include<stdio.h> #include<string.h> int main(void) { int i=0; int s; char ch; while((ch=getchar())!='#')

C++primer plus 程式設計練習

本人用code::block 編寫,如需參考,善用Ctrl+shift+C 和 Ctrl + shift + X 快捷鍵 如有任何錯誤或疑問,歡迎留言 #include <iostream> using namespace std; double p

廣工 AnyviewC C語言習題

Anyview 第七章 /********** 【習題7.010】寫一函式求3個整數中最小的數。 **********/ int min(int x, int y, int z) /* 返回3個整數x,y和z中最小的數 */ { int min=x; if (y

c++ primer plus 課後題答案

ota average ray ble splay numeric play _array using #include <iostream> using namespace std; double HAR_AVG(double, double); void

結構體的處理(以c++primer plus 習題4為例)

類型 數據 frame bsp 傳遞 sign 習題 bre xxxxxxxx 1 const unsigned int strsize = 50; 2 struct bop //結構體就像一個數據類型如int 使用前應該先給他一個變量如本題中的bop 3

c++ Primer Plus5課後習題程式碼

#include <iostream> #include <array> #include <string> #include <cstring> using namespace std; void test1() { in

C++ primer plus 課後習題,原創答案。

//第十章第一題 #ifndef GOLF_H_ #define GOLF_H_ #include<string> #include<iostream> using std::string; class BankCount { pri

C++Primer習題十二

我終於快要跟上我之前看書的進度了!!!感覺做題做了好久。。。 練習12.1:在此程式碼的結尾,b1和b2各包含多少個元素? StrBlob b1; { StrBlob b2 = { "a", "an", "the" }; b1 = b2; b2.push_back( "ab

c++primer plus 11 編程題7題

eal 習慣 ios 對象 def 臨時 () n) rim #pragma once #ifndef COMPLEX0_H_ #define COMPLEX0_H_ #include<iostream> class Complex { private:

c++primer plus 13 編程題2題

pointer eth close spl UNC rim 自身 inter object #pragma once #ifndef CD_H_ #define CD_H_ //base class class Cd { private: char * per

C Primer Plus 編程練習

思考 reat enter span n) 試驗 val unsigned 小數 1.通過試驗(即編寫帶有此類問題的程序)觀察系統如何處理整數上溢、浮點數上溢和浮點數下溢的情況。 #include<stdio.h> int main(void) {

JavaSE習題 常用實用類

問答題 1.怎樣例項化一個Calendar物件? Calendar ca=Calendar.getInstance(); 2.Calendar物件呼叫set(1949,9,1)設定的年月日分別是多少? 就是1949,9,1 3.怎樣得到一個1—100的隨機數? int a=(int) (Ma

c++ primer 筆記十三拷貝控制

13章 拷貝控制 一個類定義五中特殊成員函式:拷貝建構函式、拷貝賦值運算子、移動建構函式、移動賦值運算子和解構函式。叫做拷貝控制操作。 13.1 拷貝、賦值與銷燬 13.1.1 拷貝建構函式 拷貝函式第一個引數是自身型別的引用,一般是常量引用,且額外引數都有預設值。 拷貝函式通常

C++ primer 5th : 筆記

第三章 : 字串 , 向量 , 陣列   名稱空間 :    [ 標頭檔案中不包含using 的宣告 ]     using namespce std;     std::cout   sting 型別:     構造 :        操作 :

C++ primer 5th : 筆記

第四章: 表示式   基本概念:     運算子:  一元 , 二元 , 三元     組合運算子 和 運算物件 :          優先順序:  使用  () 避免優先順序的混淆的問題         結合律:           求

C++Primer——《 》語句

● 一個表示式,比如 ival+5, 末尾加上分號就變成了表示式語句。表示式語句的作用是執行表示式並丟棄掉求值結果。 ● 空語句中只含有一個單獨的分號: ; 如果在程式的某個地方,語法上需要一條語句但是邏輯上不需要,此時應該使用空語句。當迴圈的全部工作在條件部分就可以完成時