1. 復(fù)習(xí)內(nèi)容
1.1 匿名命名空間
字面意思:聲明命名空間時忽略名字
編譯器內(nèi)部會為這個命名空間生成一個唯一的名字和using指令
namespce {
char c;
int i;
double d;
}
所以上面的代碼在編譯器內(nèi)部類似于:
namespace __UNIQUE_NAME_ {
char c;
int i;
double d;
}
using namespace __UNIQUE_NAME_;
它和靜態(tài)變量的相似之處筹陵?
匿名命名空間也具有內(nèi)連接屬性,也就是說名稱的作用域被限制在當(dāng)前文件中贱枣,無法在其他文件使用extern來擴展作用域
它和靜態(tài)變量相比更優(yōu)在哪适袜?
對于多個同一文件的標(biāo)識符函數(shù)只需要用一個匿名空間來聲明欢峰,不需要多次輸入static
1.2 如何引用命名空間
// 方式一
ace::Mutex mutex;
// 方式二
using ace::Mutex;
// 方式三
using namespace ace;
-
方式一:
只是在必要的時候運用域運算符::來引用指定空間里的標(biāo)識符炊邦。
適用于:當(dāng)前編譯單元內(nèi)引用ace內(nèi)的標(biāo)識符不多且使用次數(shù)不多 -
方式二:
只引入ace::Mutex一個標(biāo)識符
適用于:當(dāng)前編譯單元內(nèi)ace::Mutex使用次數(shù)較多的情況 -
方式三:
把ace里的全部標(biāo)識符都引入當(dāng)前命名空間中芙粱,此后ace內(nèi)所有的標(biāo)識符對于當(dāng)前空間都是可見的
適用于:當(dāng)前編譯單元內(nèi)使用ace內(nèi)的標(biāo)識符較多划咐,且不會出現(xiàn)標(biāo)識符沖突的問題
對于上面三種方式的選擇應(yīng)由一到三拴念,因為越往后產(chǎn)生命名沖突的可能越大
1.3 override與final
-
override:指定一個虛函數(shù)覆寫另一個虛函數(shù)
在成員聲明或定義中,override確保該函數(shù)為虛并且復(fù)寫來自基類的虛函數(shù)褐缠,若不是就會出現(xiàn)編譯錯誤 -
final:指定派生類不能覆寫虛函數(shù)政鼠,或類不能被繼承
在虛函數(shù)聲明或定義中,final確保函數(shù)為虛且不可被派生類復(fù)寫队魏。
在類定義中缔俄,final指定此類不能被派生
1.4 訪問者模式
-
定義:封裝某些作用于某數(shù)據(jù)結(jié)構(gòu)中各元素的操作,它可以在
不改變數(shù)據(jù)結(jié)構(gòu)
的前提下定義作用于這些元素的新的操作 - 自己的理解:在衣服店定制了幾套衣服,成衣后我又想對某件衣服進(jìn)行改變俐载,但是已經(jīng)不能改變衣服了蟹略,所以收到衣服后,我只能針對我想改變的那件遏佣,新增加一些小搭配(絲巾等等)
-
含有角色:
抽象訪問者 訪問者 抽象元素類 元素類 結(jié)構(gòu)對象
package yanbober.github.io;
import java.util.ArrayList;
import java.util.List;
//Vistor(抽象訪問者)
interface Vistor {
void visit(ConcreteElementNodeA node);
void visit(ConcreteElementNodeB node);
}
//ConcreteVisitor(具體訪問者)
class ConcreteVisitorA implements Vistor {
@Override
public void visit(ConcreteElementNodeA node) {
System.out.println(node.operationA());
}
@Override
public void visit(ConcreteElementNodeB node) {
System.out.println(node.operationB());
}
}
class ConcreteVisitorB implements Vistor {
@Override
public void visit(ConcreteElementNodeA node) {
System.out.println(node.operationA());
}
@Override
public void visit(ConcreteElementNodeB node) {
System.out.println(node.operationB());
}
}
//Element(抽象元素)
abstract class ElementNode {
public abstract void accept(Vistor vistor);
}
//ConcreteElement(具體元素)
class ConcreteElementNodeA extends ElementNode {
@Override
public void accept(Vistor vistor) {
vistor.visit(this);
}
public String operationA() {
return "ConcreteElementNodeA";
}
}
class ConcreteElementNodeB extends ElementNode {
@Override
public void accept(Vistor vistor) {
vistor.visit(this);
}
public String operationB() {
return "ConcreteElementNodeB";
}
}
//ObjectStructure(對象結(jié)構(gòu))
class ObjectStructure {
private List<ElementNode> nodeList = new ArrayList<>();
public void action(Vistor vistor) {
for (ElementNode node : nodeList) {
node.accept(vistor);
}
}
public void add(ElementNode node) {
nodeList.add(node);
}
}
//客戶端
public class Main {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.add(new ConcreteElementNodeA());
objectStructure.add(new ConcreteElementNodeB());
Vistor vistor = new ConcreteVisitorA();
objectStructure.action(vistor);
}
}
參考:
設(shè)計模式(行為型)之訪問者模式(Visitor Pattern)
23種設(shè)計模式(9):訪問者模式
1.5 何時捕獲異常
我們只能捕獲我們能夠處理的異常挖炬,能夠恢復(fù)的異常將它捕獲后恢復(fù),像不能恢復(fù)的如状婶,越界等就不要捕獲了意敛。
對使用 C++ 異常處理應(yīng)具有怎樣的態(tài)度?
1.6 可重入函數(shù)
1.7 雜項
- explicit構(gòu)造函數(shù):指定構(gòu)造函數(shù)或轉(zhuǎn)換函數(shù)為顯式膛虫,即它不能用于隱式轉(zhuǎn)換和復(fù)制初始化
- 靜態(tài)存儲周期:static用于聲明對象擁有靜態(tài)存儲期
- POD(Plain Old Data):該類型包括標(biāo)量類型草姻,c/c++的基本類型,用戶自定義的類類型(這里的類必須是無析構(gòu)函數(shù)和構(gòu)造函數(shù)/拷貝構(gòu)造函數(shù)的類稍刀,其實也就相當(dāng)于struct)
- const_cast:用來移除變量的cv限定符
-
volatile:一般編譯器在訪問變量時撩独,都會對變量進(jìn)行優(yōu)化。也就是不會每一次都去變量的內(nèi)存中讀數(shù)據(jù)账月,編譯器會把變量值存在「寄存器」中综膀,在下一次查詢時直接到此取數(shù)據(jù),這樣訪問速度就大大增加局齿。
而volatile修飾的變量剧劝,編譯器每一次查詢都會去內(nèi)存中讀取數(shù)據(jù)。它常用于多線程編程抓歼,當(dāng)一個線程改變變量的值的時候讥此,「寄存器」的值可能還未更新,所以必須從內(nèi)存中讀數(shù)據(jù)讀到的才是真實的數(shù)據(jù)谣妻。 -
mutable:常用于不影響類的外部可變狀態(tài)的成員萄喳,該成員在const函數(shù)內(nèi)依舊可被修改
下面是它在lambda表達(dá)式中的使用:
雖然看起來修改了x,其實只是修改了x的拷貝
int x{0};
auto f1 = [=]() mutable {x = 42;}; // okay, 創(chuàng)建了一個函數(shù)類型的實例
auto f2 = [=]() {x = 42;}; // error, 不允許修改按值捕獲的外部變量的值
- std::function:通用多態(tài)的函數(shù)封裝器拌禾,它的實例可以存儲取胎,復(fù)制及調(diào)用任何可調(diào)用目標(biāo)
// 存儲自由函數(shù)
std::function<void(int)> f_display = print_num;
f_display(-9);
// 存儲 lambda
std::function<void()> f_display_42 = [](){ print_num(42); };
f_display_42();
2. 代碼風(fēng)格的思考(僅針對客戶端)
2.1 在別人代碼上進(jìn)行迭代
- 注意不要破壞別人原有的風(fēng)格,對函數(shù)命名/變量命名應(yīng)與前人統(tǒng)一
- 修改之前應(yīng)該仔細(xì)看看代碼的結(jié)構(gòu)湃窍,不要把本來耦合度很低的代碼改成了耦合度極高的代碼
2.2 自己寫代碼
- 在做csd時就思考好節(jié)點的構(gòu)造
- 低耦合
- 代碼簡潔闻蛀,清楚
- 命名易于理解,別人在看你代碼時您市,可以通過命名直接明白函數(shù)需要做什么
- 考慮內(nèi)存觉痛!考慮內(nèi)存!需要做緩存的就做緩存
下面以這個頁面來對代碼做一個梳理:
首先有一個頭部主要是放活動圖片/標(biāo)題等茵休,中部是一個橫向scroll薪棒,上面的btn可以刷新下面的縱向scroll里的節(jié)點信息
- 首先拿到頁面所需要信息(initData)
- 根據(jù)信息從上往下在代碼中對頁面進(jìn)行展示(initView)
- 思考scroll是否需要做緩存手蝎,在這里我只對縱向scroll做了緩存
- 初始化橫向scroll,注冊回調(diào)事件來刷新縱向scroll(initMiddleScroll,updateMiddleScroll)
- 定義兩個table:tableItemsSel 和 tableItemsSpare(用于存儲緩存數(shù)據(jù))
- 封裝縱向scroll的信息俐芯,使用一個table封裝棵介,這里的point用于等會插入緩存table(initScrollData)
local tableItemsSel = {
{ data = data,
point = nil,
pos = {x = 0,y = 0}
}
}
- 初始化縱向scroll,計算節(jié)點位置吧史,更新tableItemsSel.pos(initScroll)
- 填充縱向scroll
設(shè)定一個值為預(yù)加載范圍邮辽,如果在范圍內(nèi)就讓節(jié)點可見,不在范圍內(nèi)就讓節(jié)點不可見且插入緩存tableItemsSpare
local scrollH = scroll的高度
local tableShowH = {beginY = math.max(0,scroll的y軸偏移-0.5*scrollH),endY = scroll的y軸偏移+1.5*scrollH,}
白色區(qū)域為我們的初始可視區(qū)域贸营,紅色區(qū)域就是我們現(xiàn)在擴大了的可視區(qū)域吨述,超過這個可視區(qū)域,就讓節(jié)點不可見且插入緩存tableItemsSpare
if v.pos.y < tableShowH.beginY or v.pos.y > tableShowH.endY then
v.point:setVisible(false)
table.insert(self.tableItemsSpare,v.point)
v.point = nil
end
如果緩存數(shù)組里有節(jié)點就取出設(shè)為可見钞脂,再初始化它揣云。否則新建節(jié)點
- 設(shè)置scroll回調(diào)事件去重新填充scroll