[英]Reasons for defining non-const 'get' member functions?
我正在與Stroustrup的《使用C ++編程原理和實踐》一書一起學習C ++。 在一個練習中,我們定義一個簡單的結構:
template<typename T>
struct S {
explicit S(T v):val{v} { };
T& get();
const T& get() const;
void set(T v);
void read_val(T& v);
T& operator=(const T& t); // deep copy assignment
private:
T val;
};
然后要求我們定義一個const和一個非const成員函數來獲取val
。
我想知道:在任何情況下,讓非常量get
函數返回val
有意義嗎?
在我看來,在這種情況下我們不能間接改變價值的做法更為清潔。 需要用const和非const get
函數返回成員變量的用例可能是什么?
非常量獲取器?
Getter和Setter只是約定。 除了提供一個getter和setter之外,有時使用的慣用法是按照
struct foo {
int val() const { return val_; }
int& val() { return val_; }
private:
int val_;
};
這樣,根據實例的恆定性,您可以獲得引用或副本:
void bar(const foo& a, foo& b) {
auto x = a.val(); // calls the const method returning an int
b.val() = x; // calls the non-const method returning an int&
};
總體而言,這是否是一種好風格,尚有待商opinion。 在某些情況下,它會引起混亂,而在其他情況下,此行為正是您所期望的(請參閱下文)。
無論如何,更重要的是根據類的用途和使用方式來設計類的接口,而不是盲目地遵循有關setter和getter的約定(例如,應給該方法起一個有意義的名稱)它表達了它的作用,而不僅僅是“假裝被封裝,現在讓我通過吸氣劑訪問所有內部組件”,這就是在各處使用吸氣劑的實際含義。
具體例子
考慮到容器中的元素訪問通常是這樣實現的。 作為玩具示例:
struct my_array {
int operator[](unsigned i) const { return data[i]; }
int& operator[](unsigned i) { return data[i]; }
private:
int data[10];
};
容器的工作不是向用戶隱藏元素(即使data
可能是公開的)。 您不希望根據要讀取或寫入元素的方式來使用不同的方法來訪問元素,因此在這種情況下提供const
和非const重載是很有意義的。
get與封裝的非常量引用
也許不是那么明顯,但是是否提供getter和setter支持封裝還是相反,這引起了一些爭議。 雖然一般而言,此問題在很大程度上是基於意見的,但對於返回非const引用的getter來說,與其說意見無關,不如說是。 他們確實破壞了包圍。 考慮
struct broken {
void set(int x) {
counter++;
val = x;
}
int& get() { return x; }
int get() const { return x; }
private:
int counter = 0;
int value = 0;
};
顧名思義,該類已損壞。 客戶可以簡單地獲取一個引用,而該類沒有機會計算修改值的次數(如set
建議的)。 一旦返回非常量引用,則有關封裝的信息與公開成員幾乎沒有什么區別。 因此,這僅用於這種行為是自然的情況(例如容器)。
聚苯乙烯
請注意,您的示例返回const T&
而不是值。 這是合理的模板代碼,你不知道副本有多貴,而對於int
您不會通過返回獲益良多const int&
,而不是一個int
。 為了清楚起見,我使用了非模板示例,盡管對於模板代碼,您可能寧願返回const T&
。
首先讓我重新表述您的問題:
為什么要為成員使用非常量獲取方法,而不僅僅是使成員公開?
有幾種可能的原因:
誰說非常量獲取器必須是:
T& get() { return val; }
? 可能是這樣的:
T& get() {
if (check_for_something_bad()) {
throw std::runtime_error{"Attempt to mutate val when bad things have happened");
}
return val;
}
However, as @BenVoigt suggests, it is more appropriate to wait until the caller actually tries to mutate the value through the reference before spewing an error.
一些組織執行編碼標准。 這些編碼標准有時是由可能過分防御的人編寫的。 因此,您可能會看到類似以下內容的內容:
除非您的課程是“普通數據”類型 ,否則任何數據成員都不得公開。 您可以根據需要為此類非公開成員使用getter方法。
然后,即使特定類只允許非常量訪問是有意義的,也不會發生。
val
不在嗎? 您已經給出了一個示例,其中val
實際上存在於類的實例中。 但實際上-不必! get()
方法可以返回某種代理對象,該代理對象在賦值,變異等情況下執行一些計算(例如,在數據庫中存儲或檢索數據)。
現在,閱讀上面的項目1.或3,您可能會問“但是我的struct S
確實有val
!”。 或“通過我的get()
不會做任何有趣的事!” -好吧,是的,他們沒有; 但是您將來可能希望更改此行為。 沒有get()
,您所有類的用戶都將需要更改其代碼。 使用get()
,只需更改struct S
的實現即可。
現在,我不提倡這種設計方法,但是有些程序員支持。
get()
被允許進行突變的非const對象調用,您可以執行以下操作:
S r(0);
r.get() = 1;
但是,如果將r
const設為const S r(0)
,則行r.get() = 1
將不再編譯,甚至無法檢索該值,這就是為什么您至少需要const版本const T& get() const
為了能夠檢索const對象的值,這樣做允許您執行以下操作:
const S r(0)
int val = r.get()
的成員函數的const版本盡量與呼叫在制造的物體,也就是說,如果對象是是常量和成員函數不可改變返回一個參考,它可以通過返回反映呼叫者的常量性的常量性特性一致const引用,因此保留了對象的不變性。
這取決於S
的目的。 如果是某種薄包裝紙,則允許用戶直接訪問基礎值可能是合適的。
真實示例之一是std::reference_wrapper
。
否。如果獲取程序只是返回對成員的非常量引用,如下所示:
private:
Object m_member;
public:
Object &getMember() {
return m_member;
}
然后, m_member
應該改為public,並且不需要訪問器。 絕對沒有必要將此成員設為私有,然后創建一個訪問器,該訪問器提供對其的所有訪問權限。
如果調用getMember()
,則可以將結果引用存儲到指針/引用中,然后,可以使用m_member
進行任何m_member
,封閉的類對此一無所知。 就像m_member
已經公開一樣。
請注意,如果getMember()
執行一些其他任務(例如,它不僅返回m_member
,而是懶洋洋地構造了它),那么getMember()
可能會有用:
Object &getMember() {
if (!m_member) m_member = new Object;
return *m_member;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.