定义抽象数据类型
引入 this
成员函数通过一个名为 this 的额外的隐式参数来访问调用它的那个对象,在成员函数内部,任何对类成员的直接访问都被看做 this 的隐式调用,this 是一个常量指针(顶层 const)。
引入 const 成员函数
因为 this 默认不是底层 const,所以不能指向常量对象,将 this 设置为底层 const 有助于提高函数的灵活性。
string isbn() const {} // 将 const 关键字放在参数列表后
// 此时 this 类型为 const class_name *const
构造函数
因为构造过程需要修改成员变量的值,所以构造函数不能声明为 const 的。
合成的默认构造函数
如果存在类内初始值,用它来初始化成员;否则默认初始化该成员。
- 只有当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数
- 如果类包含内置类型或者复合类型(比如数组和指针)的成员,只有这些成员全部被赋予了类内的初始值时,这个类才适合于使用合成的默认构造函数
在 C++11 中,如果我们需要默认的行为,那么可以通过在参数列表后写上 = default 来要求编译器生成构造函数。
这样写是因为我们既需要其它形式的构造函数,也需要默认构造函数。
Sales_data() = default; // 默认构造函数
使用默认构造函数
Sales_data obj(); // 错误,obj 是一个函数
Sales_data obj; // 正确
构造函数初始化列表
// 被初始化列表忽略的成员,将以与合成默认构造函数相同的方式隐式初始化
Sales_data(const std::string &s, unsigned n): bookNo(s), units_sold(n) {}
访问控制
class 与 struct 唯一的区别是默认的访问权限不同,class 默认为 private,struct 默认为 public。
类可以允许其他类或者函数访问它的非公有成员,方法是令其成为它的友元( friend )。
// 友元声明只能在类的内部
friend std::istream &read(std::istream&, Sales_data&); // 友元函数声明
类的其他特性
一个可变数据成员永远不可能是 const,即使它是 const 对象的成员
class Screen {
private:
mutable size_t access_ctr;
public:
void Screen::some_screen() const {
++access_ctr; // 即使在 const 函数内也能修改
}
}
返回 *this 的成员函数可以被方便地调用
inline Screen& Screen::set(char c) {
content = c;
return *this;
}
myScreen.set('#').move(4, 0); // 一系列操作可以被写在一个表达式中
可以仅声明类而暂时不定义它,这种声明被称为前向声明
class Screen;
对于类型 Screen,在它声明之后定义之前是一个不完全类型
可以
- 定义指向不完全类型的指针
- 声明以不完全类型作为参数或返回值的函数,但不能定义
不可以
构造函数初始化列表不限定初始化的执行顺序,此时初始化顺序为成员在类定义中的出现顺序
class X {
int i;
int j;
public :
X(int val): j(val), i(j) {} // 错误,i 在 j 之前被初始化
};
委托构造函数
使用它所属类的其他构造函数执行它自己的初始化过程
class Sales_data {
public:
// 非委托函数使用对应的实参初始化成员
Sales_data(std::string s, unsigned cnt, double price):
bookNo(s), units_sold(cnt), revenue(cnt *price) {}
// 其余构造函数全部委托给另一个构造函数
Sales_data(): Sales_data("", 0, 0) {}
Sales_data(std::string &s): Sales_data(s, 0, 0) {}
Sales_data(std::istream &is): Sales_data() { read(is, *this); }
};
隐式的类类型转换
如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换规则,这种构造函数称作转换构造函数。
item.combine(string"12345"); // 正确,显式地转换成 string,隐式地转换成 Sales_data
item.combine(Sales_data("12345"); // 正确,隐式地转换成 string,显式地转换成 Sales_data
item.combine("12345"); // 错误,只允许一步类类型转换
若要抑制构造函数定义的隐式转换,可以将该构造函数声明为 explicit ;explicit 构造函数只能用于直接初始化。
类的静态成员
静态成员函数不与任何对象绑定在一起,也不包含 this 指针;因此静态成员函数不能声明成 const 的,也不能在 static 函数体内使用 this 指针。
当在类的外部定义静态成员时,不能重复 static 关键字,该关键字只出现在类内部的声明语句。
静态成员并不是在创建对象时被定义的,这意味着它们不是由类的构造函数初始化的,一般来说,只能在类的外部定义和初始化静态成员/
静态成员函数可以是不完全类型
class Bar {
public:
private:
static Bar mem1; // 正确,静态成员可以是不完全类型
Bar *mem2; // 正确,指针成员可以是不完全类型
Bar mem3; // 错误,数据成员必须是完全类型
};
静态成员可以作为默认实参,而非静态成员不能