深入理解C++方法重载、内联与高级用法


深入理解C++方法重载、内联与高级用法

文章插图
方法重载你可能已经注意到,你可以在一个类中写多个构造函数 , 所有这些构造函数都有相同的名字 。这些构造函数只在参数的数量和/或类型上有所不同 。你可以对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<SpreadsheetCell&>(as_const(*this).getCellAt(x, y));}基本上,你首先使用std::as_const()(定义在<utility>中)将*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 << textHolder.getText() << endl;


推荐阅读