C++自修入門實境秀、C++ Primer 5版研讀秀 49/ ~v7 類別 練習7.58





頁298

7.5.5. Aggregate Classes

7.5.5彙總類別

彙總類別提供使用者對它的直接存取權,且其具有特殊的初始化語法:

Data val1 = { 0, "Anna" };

其實就是用串列初始化(list initialization)來初始化它的資料成員

我們可以提供由大括號圍起的成員初始器串列來初始化一個彙總類別的資料成員

彙總類別的條件:

只要符合以下數點,即為彙總類別

1. 它所有的資料成員(data member)都是公開public可存取的

2. 它並沒有定義任何建構子 參見前論彙總類別。但仍有編譯器提供的合成的預設建構器。沒有定義表示沒有自訂也。 It does not define any constructors

3. 它並沒有任何類別內初始器

4. 它並不衍生自其他類別,且也沒有虛擬函式(virtual函式→中文版這裡忘了翻,但後面有翻)



可見預設初始化和值初始化(value initialize)的不同在於預設初始化是在所有值還沒有被初始化時發生,而值初始化則是在初始化後還有同類的變數未被初始化時發生,以其同類的變數若有既定的原初值來(用此值)初始化

用來初始化彙總類別(aggregate class)的初始器串列(initializer list)的元素可以比類別成員數少,但不能較其多。

52:10

頁299

練習7.52

彙總類別的串列初始器中各個資料成員的初始器順序必須與類別中定義的順序一致

1:1:10

7.5.6. Literal Classes

7.5.6字面值類別

In § 6.5.2 (p. 239 ) we noted that the parameters and return type of a constexpr function must be literal types.

常值運算式函式的參數與回傳型別都必須是字面值型別。

In addition to the arithmetic types, references, and pointers, certain classes are also literal types.

可見算術型別、參考與指標 都屬字面值型別。

字面值型別的類別可以有本身是constexpr的函式成員。

These member functions are implicitly const (§ 7.1.2 , p. 258 ).這些成員函式不用說都是常值const的。

一個彙總類別(aggregate class)的資料成員(data member)若都是字面值型別的話,它就是字面值類別。

1:21:20

非彙總類別若符合以下的條件,也會是字面值類別:

1.資料成員全屬字面值型別

2.該類別必須至少有一個常值運算式的建構器(彙總類別是沒有建構器的)

3.如果資料成員有類別內初始器,內建型別的成員的初始器就必須是常值運算式(constant expression),而如果是類別型別(class type)的話,其初始器就勿用該成員自己類別的常值運算式建構器(constexpr constructor)

4.類別必須對它的解構器(destructor)用預設的定義。

1:40:00

constexpr Constructors

常值運算式的建構器

雖然建構器不能是const,但它在字面值類別中卻可以是個常值運算式的函式。



1:45:50

A constexpr constructor can be declared as = default (§ 7.1.4 , p. 264 ) (or as a deleted function, which we cover in § 13.1.6 (p. 507 )). Otherwise,



1:49:20 建構器的要求是不能含有一個return述句



1:51:30

A constexpr constructor can be declared as = default (§ 7.1.4 , p. 264 ) (or as a deleted function, which we cover in § 13.1.6 (p. 507 )). Otherwise, a constexpr constructor must meet the requirements of a constructor—meaning it can have no return statement—and of a constexpr function—meaning the only executable statement it can have is a return statement (§ 6.5.2 , p. 239 ). As a result, the body of a constexpr constructor is typically empty. We define a constexpr constructor by preceding its declaration with the keyword constexpr :

一個constexpr建構器可以被宣告為=default (§ 7.1.4,或是作為一個deleted函式,這會在§ 13.1.6中涵蓋)。否則,一個constexpr建構器必須符合建構器的要求,這表示它可以沒有return述句,而對constexpr函式來說,代表它唯一能擁有的可執行述句是return述句(§6.5.2)。結果就是,constexpr建構器的主體通常都是空的。

Otherwise在此應該是翻成「除此之外」才是,不是「否則」。

頁300

常值運算式的建構器一定要初始化類別中每個資料成員



常值運算式的建構器(constexpr constructor)是用來產生一個常值運算式constexpr的物件,這樣的物件是用來作為常值運算式函式的參數或回傳型別用的



2:30:40

練習7.53

#include <iostream>

using namespace std;

class Debug {

public:

constexpr Debug(bool b = true) : hw(b), io(b), other(b) {

}

constexpr Debug(bool h, bool i, bool o) :

hw(h), io(i), other(o) {

}

constexpr bool any() { return hw || io || other; }

void set_io(bool b) { io = b; }

void set_hw(bool b) { hw = b; }

void set_other(bool b) { hw = b; }

private:

bool hw; // hardware errors other than IO errors

bool io; // IO errors

bool other; // other errors

};



int main() {

Debug io_sub(false, true, false); // debugging IO

//有「constexpr」還會出錯:「E1086 the object has type qualifiers that are not compatible with the member function "Debug::any" 」下同。

if (io_sub.any()) // equivalent to if(true)

cerr << "print appropriate error messages" << endl;

Debug prod(false); // no debugging during production

if (prod.any()) // equivalent to if(false)

cerr << "print an error message" << endl;



}

2:53:20

練習7.54

有指定(assign)的動作「如io=b;」,不能宣告為constexpr,常值運算式函式只能有一個return的述句,作為執行句:

a constexpr function—meaning the only executable statement it can have is a return statement (§ 6.5.2 , p. 239 ).

結果在Visual Studio 2019中測試「constexpr void set_io(bool b) { io = b; }」是可以執行無誤的。

2.4.4……constexpr Variables:……Under the new standard, we can ask the compiler to verify that a variable is a constant expression by declaring the variable in a constexpr declaration. Variables declared as constexpr are implicitly const and must be initialized by constant expressions:

Although we cannot use an ordinary function as an initializer for a constexpr

variable, we’ll see in § 6.5.2 (p. 239) that the new standard lets us define certain

functions as constexpr. Such functions must be simple enough that the compiler can evaluate them at compile time. We can use constexpr functions in the initializer of a constexpr variable.

constexpr Functions:……A constexpr function is a function that can be used in a constant expression (§ 2.4.4, p. 65). A constexpr function is defined like any other function but must meet certain restrictions: The return type and the type of each parameter in a must be a literal type (§ 2.4.4, p. 66), and the function body must contain exactly one return statement:……When it can do so, the compiler will replace a call to a constexpr function with its resulting value. In order to be able to expand the function immediately, constexpr functions are implicitly inline.

2.4.4 constexpr and Constant Expressions:

Whether a given object (or expression) is a constant expression depends on the

types and the initializers.一個東西是不是常值(的東西)是由它的型別與初始器來決定的。

int staff_size = 27; // staff_size is not a constant expression

const int sz = get_size(); // sz is not a constant expression

Although staff_size is initialized from a literal, it is not a constant expression

because it is a plain int, not a const int. On the other hand, even though sz is a

const, the value of its initializer is not known until run time. Hence, sz is not a

constant expression.

#include <iostream>

using namespace std;

class Debug {

public:

constexpr Debug(bool b = true) : hw(b), io(b), other(b) {

}

constexpr Debug(bool h, bool i, bool o) :

hw(h), io(i), other(o) {

}

constexpr bool any() { return hw || io || other; }

constexpr void set_io(bool b) { io = b; }

constexpr void set_hw(bool b) { hw = b; }

constexpr void set_other(bool b) { other= b; }//原作「hw = b;」

private:

bool hw; // hardware errors other than IO errors

bool io; // IO errors

bool other; // other errors

};



int main() {

Debug io_sub(false, true, false); // debugging IO

//有「constexpr」還會出錯:「E1086 the object has type qualifiers that are not compatible with the member function "Debug::any" 」下同。

io_sub.set_hw(true);

io_sub.set_io(true);

io_sub.set_other(true);

if (io_sub.any()) // equivalent to if(true)

cerr << "print appropriate error messages" << endl;

Debug prod(false); // no debugging during production

if (prod.any()) // equivalent to if(false)

cerr << "print an error message" << endl;

}



練習7.55

struct Data {

int ival;

string s;

};

因為string是程式庫型別,並不是字面值型別(literal types,頁66)而一個彙總類別(aggregate class)的資料成員(data member)若都是字面值型別的話,它就是字面值類別。

7.6. static Class Members

4:30:10

We say a member is associated with the class by adding the keyword static to its declaration.

4:32:40

Declaring static Members

頁301



4:45:00

Thus, each Account object will contain two data members— owner and amount . There is only one interestRate object that will be shared by all the Account objects.

可見靜態成員本身就是一個物件「object」

靜態的成員函式並不繫結(bind)到任何的類別對象(物件),因此它們也不會有this指標,也就不能被宣告為const常值的成員函式。

4:52:10

Using a Class static Member

對類別的靜態成員的使用

可藉由範疇運算子(::)來直接調用(不需要再建構一個類別的執行個體)

好像成員函式的定義的函式名稱部分一樣:

double r;

r = Account::rate(); // access a static member using the scope operator

Even though static members are not part of the objects of its class, we can use an object, reference, or pointer of the class type to access a static member:

Account ac1;

Account *ac2 = &ac1;

// equivalent ways to call the static member rate function

r = ac1.rate(); // through an Account object or reference

r = ac2->rate(); // through a pointer to an Account object

Member functions can use static members directly, without the scope operator:

成員函式調用一般成員也是不需要範疇運算子啊?!




頁302

5:3:30

Defining static Members

When we define a static member outside the class, we do not repeat the static keyword. The keyword appears only with the declaration inside the class body:

和單一引數的建構器的explicit限定一樣,不必在類別外再冠用explicit來抑制其隱含的轉型能力(頁296)

靜態資料成員竟然不能在類別內被定義與初始化?

5:16:30

Like global objects (§ 6.1.1 , p. 204 ), static data members are defined outside any function. Hence, once they are defined, they continue to exist until the program completes.



Note also that although initRate is private , we can use it to initialize interestRate . Aswith any other member definition, a static data member definition may access the private members of its class.

pdf版與google play 圖書版不同,意思沒差別:

Note also that even though initRate

is private, we can use this function to initialize interestRate. The definition of

interestRate, like any other member definition, has access to the private

members of the class.



資格修飾 (qualification)





5:34:10

最好是把對靜態資料成員的定義與其他同類別的非行內(noninline)成員函式的定義放在同一個檔案裡。



Tip:

The best way to ensure that the object is defined exactly once is to put the definition of static data members in the same file that contains the definitions of the class noninline member functions.

5:43:50

頁303

In-Class Initialization of static Data Members

static資料成員的類別內初始化

5:49:00

通常靜態資料成員是不能在類別內被初始化的,然而如果它是const常值的,特別是字面值型別的靜態成員,又另當別論:

Ordinarily, class static members may not be initialized in the class body. However, we can provide in-class initializers for static members that have const integral type and must do so for static members that are constexpr s of literal type (§ 7.5.6 , p. 299 ).



5:59:00 如果一個已被初始化的常值的(const)、或常值運算式的(constexpr)靜態成員只會被用在編譯器可以取代它成為其對應的值的地方,那麼它就不必在類別外去定義:

If the member is used only in contexts where the compiler can substitute the member’s value, then an initialized const or constexpr static need not be separately defined.



6:10:00 所以宣告和定義的關係應該是:第一次出現的叫宣告,其後再見的即定義,而不是有沒有帶初始器來判斷它們誰是宣告、誰是定義

// definition of a static member with no initializer

constexpr int Account::period; // initializer provided in the class definition

在類別本體內已有初始器的靜態資料成員,在類別外定義的就不能再賦予其初始值。

Best Practices:Even if a const static data member is initialized in the class body, that member ordinarily should be defined outside the class definition.

總之,常值的整數型別的靜態資料成員即使在類別內已初始化了,還是要在類別外將之定義。其此定義是可省略,但不是沒有必要、或不得再定義;故還是能寫就寫吧,不要偷懶。

6:18:30

static Members Can Be Used in Ways Ordinary Members Can’t

靜態的成員是獨立存在於任何類別物件之外的

As we’ve seen, static members exist independently of any other object. As a result, they can be used in ways that would be illegal for non static data members.

前向宣告(forward declaration)(頁279)可以翻為「前置宣告」或「先一步的宣告」

6:24:20

靜態資料成員可以:

1. 是不完整的型別

2. 就是它的類別型別

而一個非靜態資料成員卻不能如此——就是它類別的型別,而只能是一個指向它自己類別型別的物件的一個指標或參考,而不能像靜態成員這般可以是它類別型別的物件本身。

頁304

我們可以直接把靜態資料成員當作一個預設引數(default argument)來傳遞:

Another difference between static and ordinary members is that we can use a static member as a default argument (§ 6.5.1 , p. 236 ):





一個非static資料成員不可以被用作預設引數,因為其值是它作為成員所屬的物件的一部 分。使用一個非static資料成員作為預設引數,並不會提供要從之獲取該成員的值的物件,所以是種錯誤。

不會,應翻成「未」才好。因為未提供一個物件,如何取得其所屬成員的值呢?此是本末倒置,所以為謬也。

練習7.56

6:45:10

總之,如果一個屬性是所有類別型別物件都需要用到的,且有共享的值的,就應該考慮使其成為靜態成員。



練習7.57

8:7:59

練習7.58

7:08:00

7:10:30

非const整數型別的靜態成員不能在類別內被初始化!只有為字面值型別(literal types)的常值運算式constexpr的靜態成員,才必須在類別內初始化。

7:54:20

example.h:

#ifndef EXAMPLE_H

#define EXAMPLE_H

#include<vector>

using namespace std;//別忘了vector是 std::vector!!!

// example.h

class Example {

public:

static double rate;

static const int vecSize = 20;

static vector <double> vec;

};

#endif

example.cpp:

// example.C

#include "example.h"

#include<vector>

using namespace std;//別忘了vector是 std::vector!!!

double Example::rate = 6.5;

vector<double> Example::vec(vecSize);



在Visual Studio 2019中,不能存成「.C」檔,會視為C語言源碼檔,非C++,而編譯錯誤。

run:

#include "example.h"

#include <iostream>

using namespace std;



int main() {

Example e;

cout << e.rate << endl;

cout << e.vecSize << endl;

cout << e.vec.size() << endl;

cout << Example::rate << endl;

cout << Example::vecSize << endl;

cout << Example::vec.size() << endl;

}

留言

熱門文章