C++自修入門實境秀、C++ Primer 5版研讀秀 44/ ~v7 類別 7.3. Additional Class Features--2...





頁267

7.1.5. Copy, Assignment, and Destruction

12:00

If we do not define these operations, the compiler will synthesize them for us.

如果我們沒有定義那些運算,編譯器就會為我們合成之。

可見前面建構器處翻成「合成」是不太恰當的

應該是「代庖」、「捉刀」、「代勞」,「代我們做」的意思。所以此句可以翻成:

當我們沒有定義這些操作的時候,編譯器也會為我們代勞。(編譯器會代我們做)

Some Classes Cannot Rely on the Synthesized Versions

某些類別不能仰賴合成的版本

某些類別不能只靠編譯器代勞操作(要我們自行定義)



it is important to understand that for some classes the default versions do not

behave appropriately.很重要的一點是,對於某些類別,編譯器代勞的操作,未必適用。

In particular, the synthesized versions are unlikely to work correctly for classes that allocate resources that reside outside the class objects themselves.尤其是要配置在類別本身(本體)之外的物件資源時,(編譯器)預設的操作並不能很好地適任。

特別是,對於會在類別物件本身之外配置資源的類別來說,合成的版本不太可能正確運作。

36:00

Classes that use vectors and strings avoid the complexities involved in allocating and deallocating memory.

使用vector和string的類別可以避免配置和釋放記憶體所涉及的複雜性。

③字形結構兼音義

運用上了vectors和strings的類別在配置與解除配置記憶時,可以減輕操作時的複雜度。



45:00

When the object is destroyed, the vector member is destroyed, which in turn destroys the elements in the vector.

物件被摧毀時,那個vector成員也會被摧毀,而那個vector中的那些元素也會因此被摧毀。

這裡 in turn應翻成「跟著、隨著、隨之而來」



Warning

Until you know how to define the operations covered in Chapter 13, the resources your classes allocate should be stored directly as data members of the class.

…你的類別所配置的資源應該直接儲存作為該類別的資料成員。

以上說明了為什麼要用「資料成員(data member)」

這個資源應該都是指記憶體資源

頁268

7.2. Access Control and Encapsulation

7.2存取控制與封裝

存取管控控管和封裝



55:40

介面:

At this point, 到目前為止we have defined an interface for our class; but nothing forces users to use that interface.

注意這裡的users也是類別使用者之意

封裝與存取修飾詞(access specifiers):

Our class is not yet encapsulated—users can reach inside a Sales_data object and meddle with its implementation. In C++ we use access specifiers to enforce encapsulation:

存取指定符(access specifiers)

• Members defined after a public specifier are accessible to all parts of the program. The public members define the interface to the class.

介面一定是public

Members defined after a private specifier are accessible to the member functions of the class but are not accessible to code that uses the class.

private的成員只能由類別的成員函式存取,而類別使用者是不能用它的

The private sections encapsulate (i.e., hide) the implementation.

封裝就是遮蔽

原來要加冒號

1:15:00

可見所謂的介面並不僅僅是我們前面理解的一般函式read、print:

2:15:00 Now that the data members of Sales_data are private, our read, print, and add functions will no longer compile. The problem is that although these functions are part of the Sales_data interface, they are not members of the class.(頁269)

反而是在類別中經由public指定符指定的部分才是

The constructors and member functions that are part of the interface (e.g., isbn and combine ) follow the public specifier; the data members and the functions that are part of the implementation follow the private specifier.

存取指定符的效力是一直到下一個存取指定符出現為止,或類別定義區塊(整體)結束之後

Using the class or struct Keyword

到底是用class還是struct好呢?

We also made another, more subtle, change: We used the class keyword rather than struct to open the class definition. This change is strictly stylistic; we can define a class type using either keyword. The only difference between struct and class is the default access level.

這項變更嚴格來說只是風格上的不同,

Note:The only difference between using class and using struct to define a class is the default access level.(頁269)

原來struct與class並沒有什麼差別,僅只是在於預設的存取級別不同而已。

struct 預設是public而 class預設則是private(在尚無存取指定符(access specifiers)出現時



頁269

內部成員要全部公開,就用struct否則就用class

並不像前面以為的,有函式的是類別class,而僅只是資料成員的是struct(結構)

還以為封裝是什麼了不起的技術,原來也不過就是加了存取指定符(access specifiers)而已

練習7.16

1:37:40

練習7.17

1:43:15

練習7.18

練習7.19

一般資料成員(data member)是不會給類別使用者用的

#ifndef PERSON_H

#define PERSON_H

#include<string>

#include<iostream>

using namespace std;

struct Person{

string addressMethod()const;

string nameMethod()const;

private:

string address ;

string name;

};

istream& read(istream&,Person&);

ostream& print(ostream&,const Person&);

#endif



因為read要能存取address、name兩個資料成員,所以把read拉進來成為成員函式

#ifndef PERSON_H

#define PERSON_H

#include<string>

#include<iostream>

using namespace std;

struct Person{

//Person()=default;

Person(){

name = "someone";

address = "somewhere";

}

Person(const string name, const string& address) :name(name),address(address){}

Person(istream&);

string addressMethod()const;

string nameMethod()const;

istream& read(istream&,Person&);//read become member function

private:

string name;

string address ;

};

ostream& print(ostream&,const Person&);

#endif

並改寫 .cpp檔 read定義:

istream& Person::read(istream& is,Person& p){//read become member function

is>>p.name>>p.address;

return is;

}

2:13:00

7.2.1. Friends

存取指定符(access specifiers)

A class can allow another class or function to access its non public members by making that class or function a friend . A class makes a function its friend by including a declaration for that function preceded by the keyword 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……

};

// declarations for nonmember parts of the Sales_data interface

頁270

Sales_data add(const Sales_data&, const Sales_data&);

std::istream &read(std::istream&, Sales_data&);

std::ostream &print(std::ostream&, const Sales_data&);

class和struct定義類別,主體末後都有一個分號。

可見一個介面分類別內的(成員)和外的(非成員)

friend的宣告一定要在類別定義本體之內,但可以隨處置放:

Friend declarations may appear only inside a class definition; they may appear anywhere in the class.

既然是自己人,當然就可以存取它內部的成員了

friend就是自己人的意思

Friends are not members of the class and are not affected by the access control of the section in which they are declared.

朋友當然不是下屬。雖然定義在類別內,但並不受其約束。



通常在類別內的朋友宣告是放在一起的,或在類別定義的開頭,或在類別本體的末後。



2:34:40

封裝的好處:

2:36:00 可見資料成員(data member)一般都是private

• User code cannot inadvertently corrupt the state of an encapsulated object.

• The implementation of an encapsulated class can change over time without requiring changes in user-level code.

封裝就類似窖藏的概念。

封裝起來類別作者就可以隨心所欲地去修改原寫作、設計的類別定義的程式碼,而不需要去顧慮到全局,投鼠忌器,並不會牽一髮而動全身。 2:45:50

Another advantage of making data members private is that the data are protected from mistakes that users might introduce.

可見封裝在一定的意義上就是對類別的資料成員(data member)作private的限定

2:52:00

編譯與寫(修改)程式碼是不一樣的。編譯是在修改後才做的。所以當然是兩件事了。

Declarations for Friends

3:4:00

頁271

練習7.20

3:6:10

3:22:00

練習7.21

Run:

#include "Person.h"

#include <iostream>

using namespace std;

int main() {



Person p;

if (read(cin, p))

{

print(cout,p)<<endl;

while (read(cin, p))

print(cout, p) << endl;

}

else

{

cerr << "No data?!" << endl;

}

}



Person.h:

#ifndef PERSON_H

#define PERSON_H

#include<string>

#include<iostream>

using namespace std;

struct Person{

//public:

//Person()=default;

Person(){

name = "someone";

address = "somewhere";

}

Person(const string name, const string& address) :name(name),address(address){}

Person(istream&);

string addressMethod()const;

string nameMethod()const;

private:

string name;

string address ;



friend istream& read(istream&, Person&);//read become member function

friend ostream& print(ostream&, const Person&);



};

istream& read(istream&,Person&);//read become member function

ostream& print(ostream&,const Person&);

#endif

Person.cpp

#include "Person.h"



Person::Person(istream& is)

{

read(cin, *this);

}



string Person::addressMethod() const

{

return address;

}



string Person::nameMethod() const

{

return name;

}

istream& read(istream& is,Person& p){//read become member function

is>>p.name>>p.address;

return is;

}

ostream& print(ostream& os,const Person& p){

os<<p.nameMethod()<<'\t'<<p.addressMethod();

return os;

}

3:24:25

練習7.22

3:30:20

7.3. Additional Class Features

可見這裡的額外(Additional)是對誰額外?對Sales_data這個類別額外 ⑧找對主詞 :

In this section, we’ll cover some additional class-related features that Sales_data doesn’t need to use.

3:38:50

7.3.1. Class Members Revisited

再談談類別成員

7.3.1再訪類別成員

Screen與Window_mgr

Defining a Type Member

型別成員(type member)

3:48:10

In addition to defining data and function members, a class can define its own local names for types. Type

除了定義資料和函式成員,一個類別也能為型別定義自己的區域名稱。

可見除了資料成員(data member)與成員函式,還有型別成員。所謂,其實就是在類別中對某一型別作別名alias宣告,而在該類別中使用此別名,當作其成員。

class Screen {

public:

typedef std::string::size_type pos;

private:

pos cursor = 0;

pos height = 0, width = 0;

std::string contents;

};

We defined pos in the public part of Screen because we want users to use that name. Users of Screen shouldn’t know that Screen uses a string to hold its data.

這樣就真的是hide起來了。

By defining pos as a public member, we can hide this detail of how Screen is implemented.

真是保密到家。所以pos 就是型別成員(type member)(就是型別別名的成員)

4:52:45

可見 別名其實 就是易容術、化妝、偽裝、欺敵的……

頁272

The second point is that, for reasons we’ll explain in § 7.4.1 (p. 284 ), unlike ordinary members, members that define types must appear before they are used. As a result, type members usually appear at the beginning of the class.

類別中的成員是沒有順序必要的,除了型別成員(type member)必須先宣告了才能使用。因此,型別成員通常是寫在類別定義的開頭



Member Functions of class Screen

Screen() = default; // needed because Screen has another constructor

// cursor initialized to 0 by its in-class initializer

Screen(pos ht, pos wd, char c): height(ht), width(wd),

contents(ht * wd, c) { }

char get() const // get the character at the cursor

{ return contents[cursor]; } // implicitly inline

inline char get(pos ht, pos wd) const; // explicitly inline

Screen &move(pos r, pos c); // can be made inline later

private:

pos cursor = 0;

pos height = 0, width = 0;

std::string contents;

4:31:11

Making Members inline

4:38:00

頁273

inline // we can specify inline on the definition

Screen &Screen::move(pos r, pos c)

{

pos row = r * width; // compute the row location

cursor = row + c ; // move cursor to the column within that row

return *this; // return this object as an lvalue

}

char Screen::get(pos r, pos c) const // declared as inline in the

class

{

pos row = r * width; // compute row location

return contents[row + c]; // return character at the given column

}



Overloading Member Functions

4:52:20

成員函式與非成員函式的函式多載與函式匹配(function matching)是無二致的

4:59:20

mutable Data Members

能夠由類別使用者更動的資料成員

是針對資料成員(data member)往往是要private的權宜設施

const(其實就是readonly)和mutable是相對的



5:6:20

頁274

Initializers for Data Members of Class Type

留言

熱門文章