GithubHelp home page GithubHelp logo

bensonhuangtw / cpp-primier-chinese-notes Goto Github PK

View Code? Open in Web Editor NEW
2.0 1.0 0.0 1.8 MB

本專案大部分內容為筆者從《C++ Primer ,5th Edition》閱讀後所自行整理之學習筆記(繁體中文),建議閱讀前有一點C語言的基礎知識(指標、變數、流程控制和函式等基礎概念即可)。

License: MIT License

c-plus-plus primer c-plus-plus-11 c-plus-plus-primer stl-containers

cpp-primier-chinese-notes's People

Contributors

bensonhuangtw avatar

Stargazers

 avatar  avatar

Watchers

 avatar

cpp-primier-chinese-notes's Issues

Chapter 7 Classes

Chapter 7 Classes

7.1 Defining Abstract Data Types

7.1.2 Defining the Revised Sales_data Class

以下為本章所使用的例子。
物件(object)的使用:

Sales_data total;         // variable to hold the running sum
if (read(cin, total))  {  // read the first transaction
    Sales_data trans;     // variable to hold data for the next transaction
    while(read(cin, trans)) {      //  read the remaining transactions
        if (total.isbn() == trans.isbn())   // check the isbns
            total.combine(trans);  // update the running total
        else {
            print(cout, total) << endl;  // print the results 
            total = trans;               // process the next book
        }
    }
    print(cout, total) << endl;          // print the last transaction
} else {                                 // there was no input
    cerr << "No data?!" << endl;         // notify the user
}

類別(class)的架構:

struct Sales_data {
    // new members: operations on Sales_data objects
    std::string isbn() const { return bookNo; }
    Sales_data& combine(const Sales_data&);
    double avg_price() const;
    // data members are unchanged from § 2.6.1 (p. 72)
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
// nonmember Sales_data interface functions
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);

Defining Member Functions

類別所有的成員都必須在類別裡面宣告,然而成員函數可以定義於類別的body之外。
成員函式定義範例:

std::string isbn() const { return bookNo; }

Introducing this

當我們呼叫物件total的成員函式isbn():

total.isbn()

除了7.6提到的例外之外,我們是以該物件的身分去呼叫成員函式的。當isbn在參閱Sales_data的成員(e.g. bookNo)的時候,它暗中參考的是用來呼叫該函式之物件(此例中為total)的成員,在本次呼叫中,當isbn回傳bookNo時,它其實回傳的是total.bookNo。成員函式透過一個隱藏參數this來獲得呼叫函式之物件的成員,當我們呼叫一個成員函式,this就會被呼叫該函數之物件的位址所初始化,也就是說在本例中,編譯器將total的位址暗中傳給isbn中的this,可以想像成:

// pseudo-code illustration of how a call to a member function is translated
Sales_data::isbn(&total)

在成員函數中,我們可以直接參照該物件的成員而不用透過member access operator,所有對成員的直接使用其實都是暗中透過this,也就是說當isbn使用bookNo的時候,它暗中使用了this所指向的成員,就像是被寫成

std::string isbn() const { return this->bookNo; }

由於this就是想參照該物件本身,因此this是一個const pointer

Introducing const Member Functions

默認情況下,this是一個指向nonconst class type的const pointer,因此當物件為const的時候,將該物件綁定到this是錯誤的,這代表著我們不能呼叫它一般的成員函式(因為它們的隱藏版參數this無法被初始化),想要排除這個問題,我們希望能把this宣告成const Sales_data *const,然而this是隱藏起來的,所以C++提供的解決辦法就是讓我們在函式參數列的後方加上const,這表示說this是一個pointer to const,用這種方式的成員函式我們稱作為cosnt member function,也就是說我們可以把isbn想成:

// pseudo-code illustration of how the implicit this pointer is used
// this code is illegal: we may not explicitly define the this pointer ourselves
// note that this is a pointer to const because isbn is a const member
std::string Sales_data::isbn(const Sales_data *const this)
{ return this->isbn; }

然而當我們這麼做,就代表isbn只能讀取而無法改寫該物件的成員函數了。
Note
const的物件或是對cosnt物件的pointer或reference只能使用const member functions。

Defining a Member Function outside the Class

當我們在類別外面定義物件時,必須包含該類別的名稱:

double Sales_data::avg_price() const {
    if (units_sold)
        return revenue/units_sold;
    else
        return 0;
}

Defining a Function to Return “This” Object

combine函式想要如同內建的+=一樣運作,一般來講,當我們定義類似於內建運算子的函式時,我們應該模仿該運算子的行為,內建的+=會回傳它左側的運算元,而且為左值,因此combine回傳的必須是一個reference,由於它左邊的運算元是Sales_data物件,它的回傳型別應該是Sales_data&,因此我們透過dereference this來獲得該物件的reference:

Sales_data& Sales_data::combine(const Sales_data &rhs)
{
    units_sold += rhs.units_sold; // add the members of rhs into
    revenue += rhs.revenue;       // the members of ''this'' object
    return *this; // return the object on which the function was called
}

7.1.3 Defining Nonmember Class-Related Functions

那些概念上是類別的一部份,但實際上卻定義在類別之外的函數通常都會宣告(但不定義)於和該類別同一個的標頭檔中。

Defining the read and print Functions

// input transactions contain ISBN, number of copies sold, and sales price
istream &read(istream &is, Sales_data &item)
{
    double price = 0;
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}
ostream &print(ostream &os, const Sales_data &item)
{
    os << item.isbn() << " " << item.units_sold << " "
       << item.revenue << " " << item.avg_price();
    return os;
}

有兩個值得注意的地方,首先readprint分別使用了reference to對應的IO class type,然而IO class是無法被複製的,因此它們被passed by reference,此外由於讀跟寫入一個stream會改變該stream,因此兩個函式都使用一般的references,而非reference to const。第二個點是print最後並沒有換行,一般來說用來當輸出的函式會在格式上做最少的限制,這是為了讓使用者能自己決定是否要換行。

Defining the add Function

Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
    Sales_data sum = lhs;  // copy data members from lhs into sum
    sum.combine(rhs);      // add data members from rhs into sum
    return sum;
}

在第三行我們把sum初始化為lhs的副本,而默認下,複製一個物件會複製該物件的成員,所以說sumbookNounits_soldu以及revenue 成員值會和lhs的一樣,接下來我們呼叫combinerhs的成員值加進sum的成員值,完成後,最終回傳sum的副本。

7.1.4 Constructors

constructors負責定義class物件的data member如何被初始化,它是class內部特殊的成員函數,名子與class本身一樣,但沒有回傳型別,而且constructors不能被聲明成const member function(見7.1.2)。

The Synthesized Default Constructor

class用default constructor這種特殊的constructor來默認初始化,而且它沒有參數。如果我們沒在class裡面明確(explicitly)定義constructor,則編譯器會暗自(implicity)定義默認constructor,這種default constructor我們稱作synthesized default constructor,大部分的class中,synthesized default constructor會照下面的規則初始化data member:
(1) 如果有in-class initializer就用它來初始化成員。
(2) 如果沒有in-class initializer,則照default-initialize的規則初始化成員。
e.g.
Sales_data有提供in-class initializer給units_sold revenue,synthesized default constructor會用它們來初始化這些成員,而bookNo則會被默認初始化成空字串。

Some Classes Cannot Rely on the Synthesized Default Constructor

只有很簡單的class才會依賴synthesized default constructor,否則基於下面的主要理由,class必須定義自己的default constructor:
(1) 編譯器只有在沒有其他constructor定義的情況下才會產生synthesized default constructor,如果已經有定義其他constructor則除非我們自己定義default constructor,否則不會有。
(2) 有些默認初始化會產生一些問題(見2.2.1),可能會產生未定義的值。
(3) 有些類別可能根本無法產生synthesized default constructor,比如說該類別有一個成員是class type但它根本就沒有default constructor時,那編譯器根本無法將那個成員初始化,因此需要定義default constructor。

Defining the Sales_data Constructors

struct Sales_data {
    // constructors added
    Sales_data() = default;
    Sales_data(const std::string &s): bookNo(s) { }
    Sales_data(const std::string &s, unsigned n, double p):
               bookNo(s), units_sold(n), revenue(p*n) { }
    Sales_data(std::istream &);
    // other members as before
    std::string isbn() const { return bookNo; }
    Sales_data& combine(const Sales_data&);
    double avg_price() const;
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

What = default Means?

Sales_data() = default;

其實就是在叫default constructor做synthesized default constructor會做的事情。

Warning
如果你的編譯器不支援in-class initializers,你的default constructor應該要使用下面所說的constructor initializer list

Constructor Initializer List

接下來看另外兩個被定義於class裡面的constructors:

Sales_data(const std::string &s): bookNo(s) { }
Sales_data(const std::string &s, unsigned n, double p):
           bookNo(s), units_sold(n), revenue(p*n) { }

從上面的冒號與花括號之間的部分稱為constructor initializer list,它標明了該物件一個或多個data member在被創建時的初始值,其語法是成員名稱加上包含該成員初始值的括號(或花括號),如果某個成員在constructor initializer list被忽略,則會依照synthesized default constructor的規則初始化。

Defining a Constructor outside the Class Body

Sales_data::Sales_data(std::istream &is)
{
    read(is, *this); // read will read a transaction from is into this object
}

如我要在class外面定義constructor,就必須標明該class的名稱,雖然在本次的定義中constructor initializer list是空的,然而該物件的成員卻仍在constructor body執行前就已經被初始化了(透過in-class initializer或默認初始化),之後才藉由read來修改成員的值。

7.1.5 Copy, Assignment, and Destruction

出現時機:
(1) copy
(a) 初始化變數(見13.1.1)
(b) 藉由pass by value傳入或回傳物件
(2) assign:使用assignment operator
(3) destroy:當物件不再存在時,例如local object在離開它被創造的block時。
如果我們沒有定義以上這些運算,則編譯器會替我們合成,舉例來說如果我們使用:

total = trans;               // process the next book

則它像是在執行:

// default assignment for Sales_data is equivalent to:
total.bookNo = trans.bookNo;
total.units_sold = trans.units_sold;
total.revenue = trans.revenue;

在13章我們會學到如何定義這些運算。

Some Classes Cannot Rely on the Synthesized Versions

使用動態記憶體管理的類別通常無法依靠編譯器提供的操作,不過如果是有用到vector或是string的則能正確的進行操作。

7.2 Access Control and Encapsulation

access specifiers:
public:可供整個程式使用的部分,定義了類別的介面(interface)
private:僅供成員函數使用,無法被類別使用者用,將實作給封裝(Encapsulation)
一個類別可以有多個access specifiers,也可以沒有,某個access specifiers的作用維持到下一個access specifiers出現為止。
e.g.

class Sales_data {
public:            // access specifier added
    Sales_data() = default;
    Sales_data(const std::string &s, unsigned n, double p):
               bookNo(s), units_sold(n), revenue(p*n) { }
    Sales_data(const std::string &s): bookNo(s) { }
    Sales_data(std::istream&);
    std::string isbn() const { return bookNo; }
    Sales_data &combine(const Sales_data&);
private:            // access specifier added
    double avg_price() const
        { return units_sold ? revenue/units_sold : 0; }
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

Using the class or struct Keyword

classstruct唯一差別在於default access level,如果使用struct,則在第一個access specifiers出現前所定義的成員為public,如果使用class,則為private

7.2.1 Friends

當我們把Sales_data的部分成員設為private後,read以及print等函式就不能被編譯了,這是因為這些函式並非Sales_data的成員,無法使用其private成員,為了讓一個類別能夠允許其他類別或函式使用它的非public成員,我們將它令為friend,即透過引進該函式的宣告並在前面加上friend:

class Sales_data {
// friend declarations for nonmember Sales_data operations added
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream &read(std::istream&, Sales_data&);
friend std::ostream &print(std::ostream&, const Sales_data&);
// other members and access specifiers as before
public:
    Sales_data() = default;
    Sales_data(const std::string &s, unsigned n, double p):
               bookNo(s), units_sold(n), revenue(p*n) { }
    Sales_data(const std::string &s): bookNo(s) { }
    Sales_data(std::istream&);
    std::string isbn() const { return bookNo; }
    Sales_data &combine(const Sales_data&);
private:
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
// declarations for nonmember parts of the Sales_data interface
Sales_data add(const Sales_data&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);

friend宣告只能出現在class的定義裡面(任意的位子)。

Declarations for Friends

friend宣告並非一般的宣告,如果想要使用者能夠呼叫它們的話,我們必須在friend宣告之外再對函式做宣告。
Note
很多編譯器不會強制你在class裡面對某個函式做friend宣告前就必須先在class外面先宣告該函式,不過還是建議這麼做以防編譯環境改變。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.