C++ 为什么要使用函数而不是对成员的引用?
我正在测试一些代码,并注意到类似于:C++ 为什么要使用函数而不是对成员的引用?,c++,class,c++11,reference,C++,Class,C++11,Reference,我正在测试一些代码,并注意到类似于: template<typename T> class example{ public: example(T t): m_value{t}{} const T &value = m_value; private: T m_value; }; 模板 课例{ 公众: 示例(T T):m_值{T}{} 常数T&value=m_值; 私人: T m_值; }; 我以前没见过这个
template<typename T>
class example{
public:
example(T t): m_value{t}{}
const T &value = m_value;
private:
T m_value;
};
模板
课例{
公众:
示例(T T):m_值{T}{}
常数T&value=m_值;
私人:
T m_值;
};
我以前没见过这个。我以前使用过的几乎每个API或库都定义了一个函数,该函数返回一个成员变量,而不是对它的常量引用:
template<typename T>
class example{
public:
example(T t): m_value{t}{}
const T &value() const{
return m_value;
}
private:
T m_value;
};
模板
课例{
公众:
示例(T T):m_值{T}{}
常量T&值()常量{
返回m_值;
}
私人:
T m_值;
};
为什么第一种方式不那么常见?缺点是什么?返回适当引用的(内联)函数之所以更好,有几个原因:
请注意,由于内联,除了可能稍大的二进制文件外,函数通常不会产生任何额外的成本。第一个选项需要额外的内存,就像指针一样 如果您这样做:
inline const T& value() const{
return m_value;
}
在不需要额外内存的情况下,您可以获得与第一种方法相同的好处
另外,由于第一种方法需要C++11,人们使用它的可能性较小。返回常量引用的一般用法:
在创建或销毁副本成本高昂的情况下,返回常量引用是很常见的。
一般来说,规则是:如果传递和使用引用比复制更昂贵,不要这样做。如果您没有被要求提供子对象,通常甚至是不可能的。
否则,返回常量引用是一种有效的性能优化,这对行为良好的源代码是透明的。
很多模板代码返回常量引用,即使在上面的测试没有指出的地方,只是为了平等地对待所有的专业化,因为函数非常小,几乎可以保证是内联的 现在,让我们来看看你好奇的发现(从未见过类似的东西):
+不需要访问器函数(尽管如此,这还是很蹩脚,无论如何都会编译出来)
-对象更大(引用通常需要与指针一样多的空间)
-由于上述原因,可能需要更好的对齐。
-没有魔法成员函数,因为编译器不知道如何复制引用
-对于调试生成,不能添加其他检查 同样的外观和感觉,没有附带的损害,可以实现像这样的顺便说一句(只有额外的检查调试保持不可能):
模板
结构示例{
示例(T T):值{T}{}
联合{
常数T值;
结构{
私人:
T值;
朋友班榜样;
}_值;
};
};
封装
对于面向对象编程的纯粹主义者来说,m_值
是一个实现细节。类example
的使用者应该能够使用单个可靠的接口访问value()
,而不应该依赖于example
如何确定它。示例的未来版本(或复杂的模板专门化)可能希望在返回值()之前使用缓存或日志记录;或者,由于内存限制,它可能需要动态计算value()
如果您最初不使用访问器函数,那么如果您以后改变主意,那么使用example
的所有内容可能都必须更改。这会带来各种各样的浪费和虫子。通过提供像value()
这样的访问器,更容易将其进一步抽象一级
另一方面,有些人对这些面向对象的原则并不那么严格,他们喜欢编写高效易读的代码,在重构发生时处理重构。这只是风格而已。有些人只是讨厌任何类型的公共成员,有些人认为如果它们是不可变的/constant就可以了。一个主要的区别是,在第二个版本中,调用方不能使用引用来修改m_值
。升级该功能以执行一些检查或预获取操作也很容易。在C++11之前,您必须在所有构造函数中初始化引用,这很繁琐,第二种方法导致对象smaller@MattMcNabb在第一个示例中,您怎么说他们可以通过常量编辑m_值
?@MattMcNabb这两种样式都允许通过常量转换修改成员。通常不需要为相同功能使用更多内存?@Deduplicatorconst T&value=m_值
是类型为T const&
?@CoffeeandCode的成员变量,我认为数字5是真正的杀手(如果没有你的评论,我会把它弄错的^^^),我只想补充一下,这个示例不再返回副本;为了使这两个例子更加相似,我个人认为第6点从长远来看是最有问题的。这里使用函数的意义实际上是因为代码的演变。每次对类型进行可能的改进或必要的更改时,如果ac
template<typename T>
struct example {
example(T t): value{t}{}
union{
const T value;
struct{
private:
T value;
friend class example<T>;
} _value;
};
};