C++自修入門實境秀、C++ Primer 5版研讀秀 45/ ~v7 類別 7.3. Additional Class Features-20...
頁274
Initializers for Data Members of Class Type
That value is created by the Screen constructor that takes two size parameters and a character to create a blank screen of the given size.
那個值由接受兩個大小參數及一個字元的Screen建構是所創建,來建立一個具有給定大小的空白畫面。
「是」應是「式」的錯字。然本書均譯為「建構器」也
As we’ve seen, in-class initializers must use either the = form of initialization (which we used when we initialized the the data members of Screen) or the direct form of initialization using curly braces (as we do for screens).
類別內的初始器一定要是=或{}。
Note When we provide an in-class initializer, we must do so following an = sign or inside braces.
類別內的初始器一定是跟在「=」後面或夾在大括號{}中。
25:10
頁275
練習7.23
Screen.h:
#pragma once
#ifndef Screen_H
#define Screen_H
#include<string>
using namespace std;
class Screen
{
public:
typedef string::size_type pos;
Screen() = default;
Screen(pos w,pos h,char c ) :w(w),h(h),contents(h*w,c){}
Screen(pos w,pos h,string s ) :w(w),h(h),contents(s){}
char get() { return contents[curser]; }
string get(pos startposition,pos length) const; //不能用inline
void some_member() const;
private:
mutable size_t access_ctr=0; // may change even in a const object
// other members as before
pos curser = 0;
pos w = 0, h = 0;
string contents;
};
#endif // !Screen_H
Screen.cpp:
#include "Screen.h"
string Screen::get(pos startPos, pos length)const //不能用inline
{
pos x = startPos + length;
string s;
if (x<contents.size())
{
for (size_t i = startPos; i < x; i++)
{
s.push_back( contents[x]);
}
return s;
}
return "";
}
inline
void Screen::some_member() const
{
++access_ctr; // keep a count of the calls to any member function
// whatever other work this member needs to do
}
Run:
#include "Screen.h"
#include <iostream>
using namespace std;
int main() {
Screen sn=Screen(10,5,'a');
Screen sn1=Screen(10,5,"好好念佛,成佛是大事,其他啥都是假的啊");
cout<<sn.get()<<endl;
cout<<sn1.get()<<endl;
cout << sn.get(2,40) << endl;
cout << sn1.get(2,22) << endl;
}
1:40:59
練習7.24
1:55:30 字元作為字串型別的初始器,原來是用{}
Screen() = default;
//Screen(pos w,pos h,char c ) :w(w),h(h),contents(h*w,c){}
Screen(pos w, pos h, char c) :w(w), h(h), contents{ c }{}//字元作為字串型別的初始器,原來是用{}
Screen(pos w,pos h,string s ) :w(w),h(h),contents(s){}
Screen(pos w,pos h,pos ctrBlanks ) :w(w),h(h),contents(ctrBlanks,' '){}
1:57:39
練習7.25
可以assign或copy,都是一般的資料成員
2:3:30
練習7.26
Sales_data.h:
#pragma once
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include<string>
using namespace std;//千萬不要忘了這個
struct Sales_data {
Sales_data() {
readSeccess = false ; bookNo = "";
revenue = 0.00; soldQ = 0; bookSize = 0.00;
}
//Sales_data() = default;//預設建構器(constructor)用這式會較Sales_data() {}多二個建構器,蓋=default由編譯器創建2個額外的合成的預設建構器(synthesized default constructors)
//Sales_data() {}//與Sales_data() = default;應是一樣的,然不能寫在最前面,會遮蔽後面的建構器
//Sales_data() :bookNo{"000-000-000" } {}
Sales_data(const string &bNo) :bookNo{bNo } {}
Sales_data(const string &bNo, const double bSize, const double rvn) :bookNo{ bNo }, bookSize{ bSize }, revenue{rvn} {}
Sales_data(const string &, const double bSize, const double, const unsigned);//在類別外定義的建構器
Sales_data(istream &);//在類別外定義的建構器
//Sales_data(istream& is){read(is, *this);}
string isbn()const;
inline double avg_price()const;
Sales_data& combine(const Sales_data&, const Sales_data&);//成員函式宣告一定要在類別內,定義可在此外
Sales_data& combine(const Sales_data&);//要改變this及其屬性值就不能再在參數列後加上const;因為加上const後this成了指向常值的常值指標,被指向的東西是不能被改變的了
private:
bool readSeccess=false;//readSeccess記錄選取成功否
string bookNo;
double revenue{ 0.00 };//總營收-營業額
unsigned soldQ{ 0 };
double bookSize{0.00};
friend ostream& print(ostream& , const Sales_data&);
friend istream& read(istream& , Sales_data& );
friend Sales_data add(const Sales_data&, const Sales_data&);
};
ostream& print(ostream& , const Sales_data&);
istream& read(istream& , Sales_data& );
Sales_data add(const Sales_data&, const Sales_data&);
#endif // !SALES_DATA_H
Sales_data.cpp:
inline double Sales_data::avg_price()const
{
return soldQ? revenue / soldQ: 0.0;
//if (soldQ>0)
//{
// return revenue / soldQ;
//}
//return 0.0;
}
Run:
#include <iostream>
#include "Sales_data.h"
using namespace std;
int main() {
Sales_data sd = Sales_data(cin);
print(cout, sd);
}
2:22:55
7.3.2. Functions That Return *this
函式只有回傳參考(reference)才是左值(lvalue)
2:33:00
Functions that return a reference are lvalues (§ 6.3.2, p. 226), which means that they return the object itself, not a copy of the object. If we concatenate a sequence of these actions into a single expression: Click here to view code image
// move the cursor to a given position, and set that character
myScreen.move(4,0).set('#');
⑧找對主詞
Had we defined move and set to return Screen, rather than Screen&, this statement would execute quite differently. In this case it would be equivalent to:
頁276
// if move returns Screen not Screen&
Screen temp = myScreen.move(4,0); // the return value would be copied
temp.set('#'); // the contents inside myScreen would be unchanged
2:42:37
Returning *this from a const Member Function
2:55:00
Note A const member function that returns *this as a reference should have a return type that is a reference to const .
Overloading Based on const
基於const的重載
為了解決const這樣的難題的重載(overload)
The non const version will not be viable for const objects; we can only call const member functions on a const object.
We can call either version on a non const object, but the non const version will be a better match.
我們只能在常值的物件上呼叫常值的成員函式;而非常值的物件卻可以呼叫常值與非常值的成員函式,只不過非常值的對非常值的物件是更加適合的。
3:20:00
頁277
3:32:00
Advice: Use Private Utility Functions for Common Code
建議:為共通的程式碼使用私有的工具函式(PRIVATE UTILITY FUNCTIONS)
如果有通用性質的函式,就盡量使用private機制封裝起來
3:43:00 不見題目只見關鍵字 one:
Some readers might be surprised that we bothered to define a separate do_display operation. After all, the calls to do_display aren’t much simpler than the action done inside do_display. Why bother? We do so for several reasons:
• A general desire to avoid writing the same code in more than one place.避免程式碼重複
• We expect that the display operation will become more complicated as our class evolves. As the actions involved become more complicated, it makes more obvious sense to write those actions in one place, not two.在同一個地方掌控、編輯會更容易也更明確
• It is likely that we might want to add debugging information to do_display during development that would be eliminated in the final product version of the code. It will be easier to do so if only one definition of do_display needs to be changed to add or remove the debugging
code.
• There needn’t be any overhead involved in this extra function call. We defined do_display inside the class body, so it is implicitly inline. Thus, there likely be no run-time overhead associating with calling do_display.
In practice, well-designed C++ programs tend to have lots of small functions such as do_display that are called to do the “real” work of some other set of functions.
some other set of functions就如大官出一張嘴,而small functions就像小官跑腿(有事弟子服其勞)。
中文版此處恐怕有翻錯:
某些讀者可能會驚訝我們竟然花費力氣定義了一個個別的do_dispiay運算。
Google翻譯是:
有些讀者可能會感到驚訝,因為我們不願意定義一個單獨的do_display操作。
to do the “real” work就是實作(implemetation)的精義。
4:3:20
7.3.3. Class Types
7.3.3類別型別
留言