方法重载
你可能已经注意到,你可以在一个类中写多个构造函数,所有这些构造函数都有相同的名字。这些构造函数只在参数的数量和/或类型上有所不同。你可以对C++中的任何方法或函数做同样的事情。具体来说,你可以通过为具有不同数量和/或类型的参数的多个函数使用同一个名称来重载一个函数或方法。例如,在SpreadsheetCell类中,你可以将setString()和setValue()都重命名为set()。类定义现在看起来像这样:
export class SpreadsheetCell {
public:
void set(double value);
void set(std::string_view value);
// 省略了一些内容以保持简洁
};
set()方法的实现保持不变。当你编写代码调用set()时,编译器会根据你传递的参数来确定调用哪个实例:如果你传递一个string_view,编译器会调用string_view实例;如果你传递一个double,编译器会调用double实例。这被称为重载解析。
你可能会试图对getValue()和getString()做同样的事情:将它们都重命名为get()。然而,这样做是不行的。C++不允许你仅基于方法的返回类型来重载一个方法名,因为在许多情况下,编译器无法确定你试图调用的是哪个方法实例。例如,如果方法的返回值没有被捕获在任何地方,编译器就没有办法知道你试图调用的是哪个方法实例。
基于const的重载
你可以基于const来重载一个方法。也就是说,你可以写两个具有相同名称和相同参数的方法,一个声明为const,另一个则不是。如果你有一个const对象,编译器会调用const方法;如果你有一个非const对象,它会调用非const重载。通常,const重载和非const重载的实现是相同的。为了避免代码重复,你可以使用Scott Meyer的const_cast()模式。
例如,Spreadsheet类有一个名为getCellAt()的方法,返回对非const SpreadsheetCell的引用。你可以添加一个const重载,返回对const SpreadsheetCell的引用,如下所示:
export class Spreadsheet {
public:
SpreadsheetCell& getCellAt(size_t x, size_t y);
const SpreadsheetCell& getCellAt(size_t x, size_t y) const;
// 代码省
Scott Meyer的const_cast()模式将const重载实现为你通常会做的那样,并通过适当的转换将非const重载的调用转发给const重载,如下所示:
const SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y) const {
verifyCoordinate(x, y);
return m_cells[x][y];
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y) {
return const_cast(as_const(*this).getCellAt(x, y));
}
基本上,你首先使用std::as_const()(定义在中)将*this(一个Spreadsheet&)转换为const Spreadsheet&。接下来,你调用getCellAt()的const重载,它返回一个const SpreadsheetCell&。然后你用const_cast()将这个转换为非const SpreadsheetCell&。
有了这两个getCellAt()的重载,你现在可以在const和非const Spreadsheet对象上调用getCellAt():
Spreadsheet sheet1 { 5, 6 };
SpreadsheetCell& cell1 { sheet1.getCellAt(1, 1) };
const Spreadsheet sheet2 { 5, 6 };
const SpreadsheetCell& cell2 { sheet2.getCellAt(1, 1) };
在这种情况下,const重载的getCellAt()并没有做太多的事情,所以你通过使用const_cast()模式并没有赢得太多。然而,想象一下,如果const重载的getCellAt()做了更多的工作;那么将非const重载转发给const重载可以避免重复那些代码。
显式删除重载
重载的方法可以被显式删除,这使你能够禁止使用特定参数调用某个方法。例如,SpreadsheetCell类有一个setValue(double)方法,可以这样调用:
SpreadsheetCell cell;
cell.setValue(1.23);
cell.setValue(123);
对于第三行,编译器将整数值(123)转换为double,然后调用setValue(double)。如果由于某种原因,你不希望setValue()使用整数调用,你可以显式删除setValue()的整数重载:
export class SpreadsheetCell {
public:
void setValue(double value);
void setValue(int) = delete;
};
有了这个改变,尝试使用整数调用setValue()的操作将被编译器标记为错误。
Ref-Qualified方法
普通类方法可以在非临时和临时类实例上调用。假设你有以下类:
class TextHolder {
public:
TextHolder(string text) : m_text { move(text) } {}
const string& getText() const { return m_text; }
private:
string m_text;
};
当然,毫无疑问,你可以在非临时实例的TextHolder上调用getText()方法。这里有一个例子:
TextHolder textHolder { "Hello world!" };
cout