静态成员
内联变量
从 C++17 开始,可以将静态数据成员声明为内联。这样做的好处是不必在源文件中为它们分配空间。这是一个例子:
class Spreadsheet {
private:
static inline size_t ms_counter {
0 };
};
使用这种类定义,可以从源文件中删除以下行:
size_t Spreadsheet::ms_counter;
常量静态数据成员
整型和枚举类型的静态常量数据成员可以在类定义中定义和初始化,甚至无需将它们设为上面提到的内联变量:
class Spreadsheet {
public:
static const size_t MaxHeight {
100 };
static const size_t MaxWidth {
100 };
};
引用数据成员
Spreadsheet 类被修改为包含一个名为 m_theApp 的新引用数据成员:
class Spreadsheet {
public:
Spreadsheet(size_t width, size_t height, SpreadsheetApplication& theApp);
private:
SpreadsheetApplication& m_theApp;
};
建议在这种情况下使用引用而不是指针,因为 Spreadsheet 应始终引用 SpreadsheetApplication。指针不能保证这一点。
引用在构造函数中提供给每个Spreadsheet。引用不能不引用某物而存在,因此 m_theApp 必须在构造函数的 ctor-initializer 中赋值。
Spreadsheet::Spreadsheet(size_t width, size_t height,
SpreadsheetApplication& theApp)
: m_id {
ms_counter++ }
, m_width {
std::min(width, MaxWidth) }
, m_height {
std::min(height, MaxHeight) }
, m_theApp {
theApp } {
// Code omitted for brevity.
}
最后,引用数据成员也可以标记为 const。例如,你可能决定 Spreadsheets 应该只具有对应用程序对象的常量引用。可以简单地更改类定义以将 m_theApp 声明为常量引用:
class Spreadsheet {
public:
Spreadsheet(size_t width, size_t height, const SpreadsheetApplication& theApp);
private:
const SpreadsheetApplication& m_theApp;
};
嵌套类
可以在另一个类定义中提供一个类定义。例如,你可能决定 SpreadsheetCell 类实际上是 Spreadsheet 类的一部分。由于它成为 Spreadsheet 类的一部分,不妨将其重命名为 Cell。你可以这样定义它们:
class Spreadsheet {
public:
class Cell {
public:
Cell() = default;
Cell(double initialValue);
};
Spreadsheet(size_t width, size_t height, const SpreadsheetApplication& theApp);
// Remainder of Spreadsheet declarations omitted for brevity
};
在任何地方引用 Spreadsheet 类之外的 Cell,都必须使用 Spreadsheet:: 范围限定名称(参数不用)。这甚至适用于方法定义。例如,Cell 的构造函数现在看起来像这样:
Spreadsheet::Cell::Cell(double initialValue) : m_value {
initialValue } {
}
你可以通过仅在 Spreadsheet 类中包括 Cell 的前向声明然后单独定义 Cell 类来缓提高代码可读性,如下所示:
class Spreadsheet {
public:
class Cell;
Spreadsheet(size_t width, size_t height, const SpreadsheetApplication& theApp);
// Remainder of Spreadsheet declarations omitted for brevity
};
class Spreadsheet::Cell {
public:
Cell() = default;
Cell(double initialValue);
// Omitted for brevity
};
普通访问控制适用于嵌套类定义。如果声明一个私有或受保护的嵌套类,则只能从外部类内部使用它。嵌套类可以访问外部类的所有受保护和私有成员。另一方面,外部类只能访问嵌套类的公共成员。
方法重载
基于const方法的重载
你可以重载基于 const 的方法。也就是说,可以编写两个具有相同名称和相同参数的方法,其中一个被声明为 const,而另一个则不是。如果你有一个 const 对象,编译器会调用 const 方法,如果你有一个非常量对象,它会调用非常量重载。
通常,const 重载和非常量重载的实现是相同的。为防止代码重复,您可以使用 Scott Meyer 的 const_cast()
模式。例如,Spreadsheet 类有一个名为 getCellAt() 的方法,它返回对 SpreadsheetCell 的非常量引用。您可以添加一个 const 重载,它返回对 SpreadsheetCell 的常量引用,如下所示:
class Spreadsheet {
public:
SpreadsheetCell& getCellAt(size_t x, size_t y);
const SpreadsheetCell& getCellAt(size_t x, size_t y) const;
// Code omitted for brevity.
};
Scott Meyer 的 const_cast() 模式像往常一样实现 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));
}
显式删除重载
可以显式删除重载方法,这使你可以禁止调用带有特定参数的方法。例如,SpreadsheetCell 类有一个方法 setValue(double),可以按如下方式调用:
SpreadsheetCell cell;
cell.setValue(1.23);
cell.setValue(123);
对于第三行,编译器将整数值 (123) 转换为双精度值,然后调用 setValue(double)。如果出于某种原因,你不希望使用整数调用 setValue(),可以显式删除 setValue() 的整数重载:
class SpreadsheetCell {
public:
void setValue(double value);
void setValue(int) = delete;
};
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() 方法。但是,也可以在临时实例上调用 getText():
cout << TextHolder{
"Hello world!" }.getText() << endl;
cout << move(textHolder).getText() << endl;
可以明确指定可以调用特定方法的实例类型,无论是临时变量还是非临时变量。这是通过向方法添加所谓的 ref-qualifier 来完成的。如果只应在非临时实例上调用方法,则在方法标头后添加 & 限定符。同样,如果只应在临时实例上调用方法,则添加 && 限定符。
以下修改后的 TextHolder 类通过返回对 m_text 的常量引用来实现 & 限定的 getText()。另一方面,&& 限定的 getText() 返回对 m_text 的右值引用,以便可以将 m_text 移出 TextHolder。例如,如果您想要从临时 TextHolder 实例中检索文本,这会更有效:
class TextHolder {
public:
TextHolder(string text) : m_text {
move(text) } {
}
const string& getText() const & {
return m_text; }
string&& getText() && {
return move(m_text); }
private:
string m_text;
};
文章评论