回答
您可以使用類似於Jon所建議的解決方案,但使用運算符重載保留普通的C++語義。我稍微修改Jon的代碼如下(解釋遵循的代碼):
#include <iostream>
template<typename T>
class Accessor {
public:
explicit Accessor(const T& data) : value(data) {}
Accessor& operator=(const T& data) { value = data; return *this; }
Accessor& operator=(const Accessor& other) { this->value = other.value; return *this; }
operator T() const { return value; }
operator T&() { return value; }
private:
Accessor(const Accessor&);
T value;
};
struct Point {
Point(int a = 0, int b = 0) : x(a), y(b) {}
Accessor<int> x;
Accessor<int> y;
};
int main() {
Point p;
p.x = 10;
p.y = 20;
p.x++;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
我們超載operator=
保留了通常的賦值語法,而不是一個函數調用的語法。我們使用演員操作符作爲「getter」。我們需要operator=
的第二個版本來允許在main()
中分配第二種類型。
現在,您可以添加到Accessor的構造函數指針或更好的函數 - 以任何方式調用getter/setters似乎是正確的。下面的示例假設setter函數返回布爾傳達協議設置新值,吸氣劑可以只修改它的出路:
#include <iostream>
#include <functional>
#include <cmath>
template<typename T>
class MySetter {
public:
bool operator()(const T& data)
{
return (data <= 20 ? true : false);
}
};
template<typename T>
class MyGetter {
public:
T operator()(const T& data)
{
return round(data, 2);
}
private:
double cint(double x) {
double dummy;
if (modf(x,&dummy) >= 0.5) {
return (x >= 0 ? ceil(x) : floor(x));
} else {
return (x < 0 ? ceil(x) : floor(x));
}
}
double round(double r, int places) {
double off = pow(10.0L, places);
return cint(r*off)/off;
}
};
template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
public:
explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}
Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
operator T() const { value = getter(value); return value;}
operator T&() { value = getter(value); return value; }
private:
Accessor(const Accessor&);
T value;
G getter;
S setter;
};
struct Point {
Point(double a = 0, double b = 0) : x(a), y(b) {}
Accessor<double> x;
Accessor<double> y;
};
int main() {
Point p;
p.x = 10.712;
p.y = 20.3456;
p.x+=1;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15.6426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 25.85426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 19.8425;
p.y+=1;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
然而,隨着最後一行表明,它有一個bug。返回T &的演員操作員允許用戶繞過設置者,因爲它允許他們訪問私有值。解決這個錯誤的一種方法是實現你希望Accessor提供的所有操作符。例如,在下面的代碼我使用了+ =運營商,因爲我脫離的鑄造操作返回參考我不得不實施operator+=
:
#include <iostream>
#include <functional>
#include <cmath>
template<typename T>
class MySetter {
public:
bool operator()(const T& data) const {
return (data <= 20 ? true : false);
}
};
template<typename T>
class MyGetter {
public:
T operator() (const T& data) const {
return round(data, 2);
}
private:
double cint(double x) const {
double dummy;
if (modf(x,&dummy) >= 0.5) {
return (x >= 0 ? ceil(x) : floor(x));
} else {
return (x < 0 ? ceil(x) : floor(x));
}
}
double round(double r, int places) const {
double off = pow(10.0L, places);
return cint(r*off)/off;
}
};
template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
private:
public:
explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}
Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
operator T() const { return getter(value);}
Accessor& operator+=(const T& data) { if (setter(value+data)) value += data; return *this; }
private:
Accessor(const Accessor&);
T value;
G getter;
S setter;
};
struct Point {
Point(double a = 0, double b = 0) : x(a), y(b) {}
Accessor<double> x;
Accessor<double> y;
};
int main() {
Point p;
p.x = 10.712;
p.y = 20.3456;
p.x+=1;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15.6426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 25.85426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 19.8425;
p.y+=1;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
你必須實現所有你要的運營商使用。
+ 1擊敗了我,但我不喜歡這種提供大量運算符重載的想法。另一種處理外部失效值的方法是提供一個不同的「被寫入」回調函數,當下一次讀取值時會調用它。 – 2010-04-02 00:08:25
考慮到你只在模板類中定義了所有這些運算符,我不認爲這太可怕了。在我看來,STL或boost代碼與此相比(以及一般來說太:))是可怕的。 但也有其他選擇。必須考慮自己的情況並據此作出選擇。 :) – conio 2010-04-04 00:52:50
你不知道。 C++不支持像C#那樣的屬性。如果你想讓代碼在set/get上運行,它必須是一個方法。
屬性真的可以在C#中運行方法。編譯器將它從你身上隱藏起來。 – 2010-04-01 19:46:31
對於這樣的行爲,我使用模板化的元訪問器。這是一個高度簡化的一個POD類型:
template<class T>
struct accessor {
explicit accessor(const T& data) : value(data) {}
T operator()() const { return value; }
T& operator()() { return value; }
void operator()(const T& data) { value = data; }
private:
accessor(const accessor&);
accessor& operator=(const accessor&);
T value;
};
典型用法是這樣的:
struct point {
point(int a = 0, int b = 0) : x(a), y(b) {}
accessor<int> x;
accessor<int> y;
};
point p;
p.x(10);
p.y(20);
p.x()++;
std::cout << p.x();
編譯器,如果你設置的東西,並有權利優化開啓通常內聯這些調用。與使用實際的getter和setter不同,它不再是性能瓶頸,無論發生什麼優化。將它擴展爲自動支持非POD或枚舉類型或允許在讀取或寫入數據時允許註冊回調是微不足道的。
編輯:如果您不想使用括號,則可以始終定義operator=()
和隱式轉換運算符。這裏有一個版本可以做到這一點,同時也添加了基本的「東西發生」回調支持:
更多編輯:好的,完全錯過了某人已經做了我的代碼的修訂版本。嘆。
當你可以定義operator =時,爲什麼使用笨重的函子風格? – 2010-04-01 23:24:16
@Ben:爲完整性添加差異。它忽略了C#中的那些垃圾。 – 2010-04-01 23:59:45
屬性是不是在C++的支持,但你可以實現它們:
1)通過使用模板
2)通過使語言的擴展和編寫自定義代碼預處理器
兩種方法都不會是一帆風順的,但這是可以完成的。
C++支持重載operator =,所以不需要擴展。 – 2010-04-01 23:21:33
@ Ben Voigt這取決於你想要實現什麼,以及你想要的屬性數量。使用大量的代碼,引入一個或兩個關鍵字並編寫代碼預處理器將是更好的主意 - 它將使代碼更具可讀性。 – SigTerm 2010-04-02 15:22:20
這是我一段時間後做的一個PoC實現,除了需要在構造函數中設置一些東西以使其良好且流暢地工作以外,其效果很好。
http://www.codef00.com/code/Property.h
這裏的用法示例:
#include <iostream>
#include "Property.h"
class TestClass {
public:
// make sure to initialize the properties with pointers to the object
// which owns the property
TestClass() : m_Prop1(0), m_Prop3(0.5), prop1(this), prop2(this), prop3(this) {
}
private:
int getProp1() const {
return m_Prop1;
}
void setProp1(int value) {
m_Prop1 = value;
}
int getProp2() const {
return 1234;
}
void setProp3(double value) {
m_Prop3 = value;
}
int m_Prop1;
double m_Prop3;
public:
PropertyRW<int, TestClass, &TestClass::getProp1, &TestClass::setProp1> prop1;
PropertyRO<int, TestClass, &TestClass::getProp2> prop2;
PropertyWO<double, TestClass, &TestClass::setProp3> prop3;
};
和這個類的一些使用...
int main() {
unsigned int a;
TestClass t;
t.prop1 = 10;
a = t.prop1;
t.prop3 = 5;
a = t.prop2;
std::cout << a << std::endl;
return 0;
}
有兩個煩惱這種方法:
- 你需要克屬性a 指向其擁有的類。
- 的語法來聲明一個屬性是 有點冗長,但我敢打賭,我可以清理 說了一下一些宏
有點尷尬,是的,但使用'this'註冊訪問器的想法很好,因爲它很容易讓屬性根據實例和類型信息玩弄技巧。 – 2010-04-02 00:20:10
您可以提供具有類似名稱的數據成員get和set方法:
class Example
{
private:
unsigned int x_;
double d_;
std::string s_s;
public:
unsigned int x(void) const
{ return x_;}
void x(unsigned int new_value)
{ x_ = new_value;}
double d(void) const
{ return d_;}
void d(double new_value)
{ d_ = new_value;}
const std::string& s(void) const
{ return s_;}
void s(const std::string& new_value)
{ s_ = new_value;}
};
雖然這種接近,因爲它需要使用「()」的每個成員,它不符合性能,微軟的語言提供具體的功能。
最接近的屬性匹配是聲明數據成員爲公共。
錯誤的是,通過重載operator =,你可以得到確切的語法(對於用戶)和爲你提供屬性的任何語言的能力。 – 2010-04-01 23:23:02
如果您不關心您的C++代碼不能用Microsoft Visual C++編譯器以外的任何其他編譯器編譯,那麼您可以使用某些編譯器的非標準擴展。
例如,下面的代碼將創建一個名爲MyProperty
的C#類屬性。
struct MyType
{
// This function pair may be private (for clean encapsulation)
int get_number() const { return m_number; }
void set_number(int number) { m_number = number; }
__declspec(property(get=get_number, put=set_number)) int MyProperty;
private:
int m_number:
}
int main()
{
MyType m;
m.MyProperty = 100;
return m.MyProperty;
}
有關此特定於Microsoft的語言擴展的更多信息,請訪問here。
- 1. 設置公共類屬性
- 2. JavaScript中的原型/類/公共屬性
- 3. 私人v/s目標C中的公共類屬性
- 4. 公共變量 - 需要的屬性C#
- 5. 公共存取VS類的公共屬性
- 6. 創建內部類的公共屬性?
- 7. IllegalAccessException內部類的公共屬性
- 8. 公共屬性和私人成員C#
- 9. 公共屬性不可子類
- 10. TypeScript Setter的公共屬性
- 11. 我可以循環訪問C++類的(公共)屬性嗎?
- 12. 公共訪問只有擁有對象的類
- 13. 這個屬性如何在ruby類中擁有多個屬性?
- 14. 類圖屬性可見性。公共屬性可以有getter和setter操作?
- 15. 從公共類C#
- 16. 類型'System.Web.UI.WebControls.GridView'沒有名爲'script'的公共屬性
- 17. 錯誤 - 類型'System.Web.UI.ScriptManager'沒有名爲'CompositeScript'的公共屬性
- 18. 類型'System.Web.UI.WebControls.Button'沒有名爲'i'的公共屬性
- 19. 具有公共屬性和函數的重構案例類
- 20. Yii2模型公共屬性
- 21. BLToolkit:公共只讀屬性
- 22. MSI公共屬性overriden
- 23. ASP.NET公共屬性ViewState
- 24. 公共屬性定義
- 25. 在oneOf之前有可能擁有共同的屬性嗎?
- 26. 內部類改性擁有類的屬性
- 27. ASP.NET C# - 如何設置UserControl中的CheckBoxList的公共屬性?
- 28. Model類中的公共var的未定義屬性?
- 29. vb.net中的另一個類的公共屬性
- 30. 有沒有辦法在公共性狀中擁有私人功能?
看看這裏的類似問題:http://stackoverflow.com/questions/2017623/forward-unbreakable-accessor-class-templates-c。我們利用C++特性甩掉了一些實現屬性的方法。 – 2010-04-01 11:30:07
平臺特定的或平臺無關的C++?在Windows中,VC++編譯器具有支持此功能的非標準擴展。請參閱下面的答案。 – 2010-04-01 20:12:21