C++类进阶

静态成员

内联变量

从 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;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值