我的关注点正在触及标题中的主题。
假设我们有一些非常简单的类
class Integer{
public:
Integer(int number) : m_value(number) {}
int value(){return m_value;}
private:
int m_value;
};
现在我们可以使用这个代表int的类,当然在使用这个类时没有更大的意义,但它只是一个例子。如果希望这个类的行为更像真实整数,那么我们应该提供强制转换运算符(不提运算符==,>,<等等),这是我的关注,因为我可以定义两个运算符:
operator T() {return m_value;}
operator T&() {return m_value;}
一些最简单的例子用它们(第一个或第二个)编译和工作,所以我们的类可以如下:
class Integer{
public:
Integer(int number) : m_value(number) {}
int value(){return m_value;}
operator T(){return m_value;} // casting operator
private:
int m_value;
};
但它可以是:
class Integer{
public:
Integer(int number) : m_value(number) {}
int value(){return m_value;}
operator T&(){return m_value;} // casting operator
private:
int m_value;
};
同样,我的问题是这两个中的哪一个是合适的?当然我不能同时拥有它们,因为编译器无法识别使用哪一个。
当然,引用转换似乎更有效,但我不确定它是否总是以它应该工作的方式工作。请以某种方式告诉我并说,为什么一个运营商比另一个运营商好?
答案 0 :(得分:3)
事实上,你需要两者并进行一些调整,编译器会知道选择哪一个。所有的魔力都在于你可以(并且应该在有意义的时候)将成员函数定义为const
, id est 可以从常量对象调用。
因此,您的Integer
类应该看起来像
class Integer
{
public:
Integer() : Integer(0) {}
Integer(int number) : m_value(number) {}
Integer(const Integer& other) : m_value(other.value()) {}
int value() const { return m_value; }
operator int() const { return m_value; }
operator int&() { return m_value; }
const Integer& operator=(const Integer& other) { m_value = other; return *this; }
private:
int m_value;
};
此定义允许Integer
类在以下情况下使用:
#include <iostream>
int main()
{
const Integer zero;
const Integer one(1);
Integer counter;
counter = zero + one;
std::cout << "counter: " << counter << std::endl;
counter++;
std::cout << "counter: " << counter << std::endl;
for (Integer i=0 ; i < counter ; ++i)
{
std::cout << "i: " << i << std::endl;
}
return zero;
}
编译&amp;运行:g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
输出:
counter: 1
counter: 2
i: 0
i: 1
答案 1 :(得分:3)
我同意上述两个答案。人们应该总是喜欢最小化直接操纵一个表示的函数的数量 对象并通过引用公开它(为什么在上面的两个答案和注释中也有解释)。当真正需要访问表示时,例如operator + =,那么可以通过固有地修改值来实现他们在班级中的第一个论点。
基于其参数值(例如+)生成新值的运算符可以在类外部定义,并在其实现中使用基本运算符。
class complex{
double re,im;
public:
complex& operator+=(complex a);//needs access to representation.
};
complex operator+(complex a,complex b)
{
complex r=a;
return r+=b; //access representation through +=
}
现在来到您的运营商,输入cast,您真的需要访问表示吗?首先,我们什么时候需要实现类型转换?举个简单的例子:
class Cents
{
private:
int m_nCents;
public:
Cents(int nCents=0)
{
m_nCents = nCents;
}
};
class Dollars
{
private:
int m_nDollars;
public:
Dollars(int nDollars=0)
{
m_nDollars = nDollars;
}
// Allow us to convert Dollars into Cents
operator Cents() { return Cents(m_nDollars * 100); }
};
据我所知,这里我们不需要访问代表,也不需要通过引用返回。
上面的所有例子都涉及一个非常小的对象。但是当处理繁重的对象时,复制操作确实非常昂贵,需要程序员来决定。如果我们想避免复制成本,我们可以传递参考和返回参考,但......
对结果的引用将作为a传递出函数 引用返回值,返回值不能是 自动变量。
由于运算符经常在表达式中使用多次,因此 结果不能作为静态局部变量。
所以,结果通常会在免费商店分配。复制
返回值通常比分配和(最终)释放免费商店上的对象更便宜(在执行时间,代码空间和数据空间中)。(#11.6 over.large #C++ Programming Language)
。
这些主题总是值得商榷,但我们需要确保的是安全性和无泄漏,这将导致更多控制,减少未来的痛苦。
答案 2 :(得分:1)
您的示例中定义的引用转换相当无用 - 您向内部成员返回可修改的引用,因此违反了封装原则。
如果你返回const引用,这不会是问题。关于int
和const int&
之间的选择,它实际上并不重要 - 它们在所有情况下的行为都完全一样。
如果您定义到某个重类的转换,这将更有趣。这样做时,您将看到两个选项 - 通过const引用返回 - 但这意味着,它不可能在返回值上调用非const限定方法。如果按值返回,则会产生复制构造函数的成本。选择取决于课堂设计。
还有一个返回引用的生命周期问题 - 但这通常不是问题,除非代码变得非常棘手(您需要将返回的引用存储为类的成员以解决此问题)。