Class 方法自调用最佳实践
Class methods self call best practice
考虑以下两个 C++ 示例(即使语言无关紧要)。
首先,Foo
class,它调用自己的方法,如 getSize()
、isPosValid()
、getElement()
.
class Foo {
private:
std::vector<int> elements_ { };
public:
size_t getSize() const {
return elements_.size();
}
bool isPosValid(const size_t pos) const {
return pos < getSize();
}
int getElement(const size_t pos) const {
if (!isPosValid(pos)) {
return 0;
}
return elements_[pos];
}
// Here we could use elements_.back(), but this is just for the example.
int getLastElement() const {
if (getSize() == 0u) {
return 0;
}
return getElement(getSize() - 1u);
}
bool operator==(const Foo& rhs) const {
return getSize() == rhs.getSize();
}
bool operator!=(const Foo& rhs) const {
return !(*this == rhs);
// OR maybe : return !operator==(rhs);
}
}
然后,Bar
class,它不调用自己的方法,但有种代码重复。
class Bar {
private:
std::vector<int> elements_ { };
public:
size_t getSize() const {
return elements_.size();
}
bool isPosValid(const size_t pos) const {
return pos < elements_.size();
}
int getElement(const size_t pos) const {
if (pos >= elements_.size()) {
return 0;
}
return elements_[pos];
}
// Here we could use this->elements_.back(), but this is just for the example.
int getLastElement() const {
if (elements_.size() == 0u) {
return 0;
}
return elements_[elements_.size() - 1u];
}
bool operator==(const Bar& rhs) const {
return elements_.size() == rhs.elements_.size();
}
bool operator!=(const Bar& rhs) const {
return elements_.size() != rhs.elements_.size();
}
}
如您所见,Foo
和 Bar
具有不同的体系结构。
这些示例非常基础,但我们可以开始看到它们都存在一些问题。
对于Foo
,getElement()
验证位置,所以如果我们在已经进行验证时调用它(如getLastElement()
),那么它会完成两次。
而对于 Bar
,有很多“代码重复”,如果我们想修改元素的访问方式,我们必须在所有进行访问的方法中进行修改。 (而不是仅仅修改 Foo
示例中的 getElement()
。
哪种设计的可维护性和可读性最好。有没有办法在不混合两种样式并保持一致的情况下解决这个设计问题?
有点题外话的问题,在 class 中的 C++ 调用运算符更好,比如 operator=(XXX)
还是这样 *this = XXX;
(比如 Foo::operator!=
例如)?
编辑:删除了评论中建议的 this->
限定符。
总的来说,第一种设计比较好。你总是想分解出通用代码(比如大小检查)并在你的 class 中重用它。您的示例很简单,但在现实世界中,检查可能不仅仅是查看大小,您可能希望将通用逻辑放在一个地方。这就是好的软件设计。
出于同样的原因,对于逻辑运算符,您总是希望根据 == 来定义 !=。对于其他逻辑运算符也是如此。
此外,使用更自然的相等表达式。使用 operator=() 函数的可读性要差得多。在我看来,即使对于经验丰富的 C++ 程序员也是如此。
它是设计的一个方面,您正在使用并继续使用它。我个人更喜欢 *this = XXX
,但是当 ThisClass& operator =(OtherClass o)
除了将类型 OtherClass
的成员分配给新值 o
之外什么都不做时,我只是直接在另一个内部访问该成员operator-/member-functions。但是我分别实现了 operator ==(..)
和 operator !=(..)
(这天真地快得多),或者我使用了一个自动实现 operator !=()
的宏,而 operator ==()
已经被我实现了(但是它如果 C++ 有一个 int operator =?=(ThisClass)
会更优雅,它像 strcmp 和 returns -1 如果较小,0 如果等于或 1 如果更大,并且自动定义其他 classic 比较运算符).
#define impl_operator_neq \
template<typename T>
bool operator(T other) { return !(*this == other);
编辑
但是我看到了一些天真的代码(这是错误的),它们实现了这样的相等运算符:
bool operator ==(Account const& o) {
return !(*this != o); // the person also tried return *this == o;
};
// ...
bool operator !=(Account const& o) {
return !(*this == o);
};
第一个本来是这样的,后来改成了那个可笑的代码:
bool operator ==(Account const& o) {
return !(strcmp(this->name, o.name));
};
所以在这段代码中最好使用operator xy(..)
来表示它正在调用当前class中的另一个运算符。然而,虽然运算符只是调用函数的语法糖,但它们可以是虚拟的并且可以在 classes 中重载,因此调用所谓的超级函数只能通过强制转换或写入 this->BaseClass::operator!=(other)
.
考虑以下两个 C++ 示例(即使语言无关紧要)。
首先,Foo
class,它调用自己的方法,如 getSize()
、isPosValid()
、getElement()
.
class Foo {
private:
std::vector<int> elements_ { };
public:
size_t getSize() const {
return elements_.size();
}
bool isPosValid(const size_t pos) const {
return pos < getSize();
}
int getElement(const size_t pos) const {
if (!isPosValid(pos)) {
return 0;
}
return elements_[pos];
}
// Here we could use elements_.back(), but this is just for the example.
int getLastElement() const {
if (getSize() == 0u) {
return 0;
}
return getElement(getSize() - 1u);
}
bool operator==(const Foo& rhs) const {
return getSize() == rhs.getSize();
}
bool operator!=(const Foo& rhs) const {
return !(*this == rhs);
// OR maybe : return !operator==(rhs);
}
}
然后,Bar
class,它不调用自己的方法,但有种代码重复。
class Bar {
private:
std::vector<int> elements_ { };
public:
size_t getSize() const {
return elements_.size();
}
bool isPosValid(const size_t pos) const {
return pos < elements_.size();
}
int getElement(const size_t pos) const {
if (pos >= elements_.size()) {
return 0;
}
return elements_[pos];
}
// Here we could use this->elements_.back(), but this is just for the example.
int getLastElement() const {
if (elements_.size() == 0u) {
return 0;
}
return elements_[elements_.size() - 1u];
}
bool operator==(const Bar& rhs) const {
return elements_.size() == rhs.elements_.size();
}
bool operator!=(const Bar& rhs) const {
return elements_.size() != rhs.elements_.size();
}
}
如您所见,Foo
和 Bar
具有不同的体系结构。
这些示例非常基础,但我们可以开始看到它们都存在一些问题。
对于Foo
,getElement()
验证位置,所以如果我们在已经进行验证时调用它(如getLastElement()
),那么它会完成两次。
而对于 Bar
,有很多“代码重复”,如果我们想修改元素的访问方式,我们必须在所有进行访问的方法中进行修改。 (而不是仅仅修改 Foo
示例中的 getElement()
。
哪种设计的可维护性和可读性最好。有没有办法在不混合两种样式并保持一致的情况下解决这个设计问题?
有点题外话的问题,在 class 中的 C++ 调用运算符更好,比如 operator=(XXX)
还是这样 *this = XXX;
(比如 Foo::operator!=
例如)?
编辑:删除了评论中建议的 this->
限定符。
总的来说,第一种设计比较好。你总是想分解出通用代码(比如大小检查)并在你的 class 中重用它。您的示例很简单,但在现实世界中,检查可能不仅仅是查看大小,您可能希望将通用逻辑放在一个地方。这就是好的软件设计。
出于同样的原因,对于逻辑运算符,您总是希望根据 == 来定义 !=。对于其他逻辑运算符也是如此。
此外,使用更自然的相等表达式。使用 operator=() 函数的可读性要差得多。在我看来,即使对于经验丰富的 C++ 程序员也是如此。
它是设计的一个方面,您正在使用并继续使用它。我个人更喜欢 *this = XXX
,但是当 ThisClass& operator =(OtherClass o)
除了将类型 OtherClass
的成员分配给新值 o
之外什么都不做时,我只是直接在另一个内部访问该成员operator-/member-functions。但是我分别实现了 operator ==(..)
和 operator !=(..)
(这天真地快得多),或者我使用了一个自动实现 operator !=()
的宏,而 operator ==()
已经被我实现了(但是它如果 C++ 有一个 int operator =?=(ThisClass)
会更优雅,它像 strcmp 和 returns -1 如果较小,0 如果等于或 1 如果更大,并且自动定义其他 classic 比较运算符).
#define impl_operator_neq \
template<typename T>
bool operator(T other) { return !(*this == other);
编辑
但是我看到了一些天真的代码(这是错误的),它们实现了这样的相等运算符:
bool operator ==(Account const& o) {
return !(*this != o); // the person also tried return *this == o;
};
// ...
bool operator !=(Account const& o) {
return !(*this == o);
};
第一个本来是这样的,后来改成了那个可笑的代码:
bool operator ==(Account const& o) {
return !(strcmp(this->name, o.name));
};
所以在这段代码中最好使用operator xy(..)
来表示它正在调用当前class中的另一个运算符。然而,虽然运算符只是调用函数的语法糖,但它们可以是虚拟的并且可以在 classes 中重载,因此调用所谓的超级函数只能通过强制转换或写入 this->BaseClass::operator!=(other)
.