c++ primer 阅读手记 第七章
1、定义在类内部的函数式隐式的inline函数;
2、常量成员函数
//使用const在类内部声明定义常量成员函数,bookNo:成员变量
std::string isbn() const { return bookNo; }
常量成员函数不会修改类的成员变量,也不能调用任何非常量成员函数;
常量对象,以及常量对象的引用或指针都只能调用常量成员函数;
3、编译器分两步处理类:首先编译成员的声明,然后才是成员的函数体(如果有)。因此,成员函数体和类中的其他成员没有次序的影响;
4、一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该与类在同一个头文件内。
5、类可以包含多个构造函数,和其他重载函数差不多。构造函数不能被声明成const。
6、某些类不能依赖于合成的默认构造函数:建议自已定义构造函数!
C++新标准中,可以自己定义默认构造函数:
//构造函数Sales_data()
Sales_data() = default; //在自定义多个构造函数时,保留不自定义构造函数时合成的默认构造函数
7、构造函数初始值列表
//构造函数初始值列表初始化成员时,并不一定按照列表顺序进行,这里不建议使用成员初始化成员。
Sales_data(const std::string &s) : bookNo(s) {}
Sales_data(const std::string &s, unsigned n, double p) : bookNo(s),units_sold(n), revenue(p * n) {}
Sales_data(const std::string &s) : bookNo(s),units_sold(0), revenue(0) {}
//构造函数比较草率的写法,不使用构造函数初始值
Sales_data::Sales_data(const string &s,unsigned cnt)
{
bookNo = s;
units_sold = cnt;
}
//如果成员是const、引用,或属于某种未提供默认构造函数的类类型,必须通过构造函数初始值列表为这些成员提供初始值。
//建议:使用构造函数初始值
8、使用class或struct关键字
这两个都能定义类,唯一区别是默认的访问权限不同;
class:第一个访问说明符前的成员都是private;
struct:第一个访问说明符前的成员都是publec;
9、友元:非类的成员函数若要访问类的private成员,则需要成为类的友元;
关键字:friend
//友元声明只能在类定义的内部;友元声明并非通常意义上的函数声明,类的用户调用友元函数需要再专门进行函数声明。
class Sales_data {
friend std::istream &read();
...
};
//类之间的友元关系
class Screen{
friend class Window_mgr; // Window_mgr 的成员可以访问Screen类的私有部分
//或令成员函数作为友元:friend void Window_mgr::clear();
//Window_mgr::clear必须在Screen类之前被声明,但不能定义。
//因为要在clear中使用Screen的成员,所以定义clear之前要先定义Screen,包括对clear的友元声明,最后定义clear。
private:
void fun();
}
class Window_mgr{
public:
void clear();
}
void Window_mgr::clear()
{
Screen s;
s.fun();
}
//每个类负责控制自己的友元类或友元函数
10、关键字:mutable
//类内声明
mutable size_t access_ctr; //可变数据成员,即使在一个const对象内也能被修改
11、当我们提供一个类内初始值时,必须以符号=或者花括号表示。
12、返回*this的成员函数:函数返回类型为引用,则返回对象本身;函数返回类型非引用,则返回对象的副本。
13、一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。
14、基于const的重载
class Screen {
public:
//根据对象是否时const重载了display函数
Screen &display() {}
const Screen &display() const {}
}
15、类类型
Sales_data item1; //默认初始化Sales_data类型的对象
class Sales_data item1; //一条等价的声明,从C语言继承而来
16、类的声明
class Screen; //Screen类的声明,在定义之前是一个不完全类型,不能创建它的对象。
17、只有当类全部完成后类才算被定义,所以一个类的成员类型不能时该类自己,但声明过未定义的情况下,允许包含指向它自身类型的引用或指针:
class Link_screen {
Link_screen *next;
}
18、如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数。
Sales_data(std::string s = "") : bookNo(s) { }
19、委托构造函数
class Sales_data
{
public:
std::string bookNo = "0";
unsigned units_sold = 0;
double revenue = 0;
// 非委托构造函数使用对应的实参初始化成员
Sales_data(std::string s, unsigned cnt, double price) : bookNo(s), units_sold(cnt), revenue(cnt * price)
{
std::cout << bookNo << "_0"<< std::endl;
}
// 其余构造函数全都委托给另一个构造函数
Sales_data() : Sales_data("默认委托", 0, 0) // 定义默认构造函数,委托给了三参数的构造函数
{
std::cout << bookNo << "_1" << std::endl;
}
Sales_data(std::string s) : Sales_data(s, 0, 0) //这种委托能传递形参的值
{
std::cout << s << "_2" << std::endl;
std::cout << bookNo << "_2" << std::endl;
}
Sales_data(float ff) : Sales_data() //这种套娃式委托,由最先被委托的构造函数一层一层执行
{
std::cout << bookNo << "_3" << std::endl;
std::cout << ff << "_3" << std::endl;
}
};
int main(int argc, char *argv[])
{
Sales_data test_Sales;
Sales_data test_Sales_single("单参数构造函数");
Sales_data test_Sales_float(0.232);
return 0;
}
/*从运行结果能看出委托构造函数的执行顺序,运行结果如下:
默认委托_0
默认委托_1
单参数构造函数_0
单参数构造函数_2
单参数构造函数_2
默认委托_0
默认委托_1
默认委托_3
0.232_3
*/
20、
Sales_data obj(); // 声明了一个函数而非对象
Sales_data obj2; // obj2是一个对象
21、隐式的类类型转换
22、聚合类,同时满足以下条件:
a,所有成员都是public的;
b,没有定义任何构造函数;
c,没有类内初始值;
d,没有基类,也没有virtuall函数。
struct Data {
int ival;
string s;
};
//可以使用花括号初始化数据成员,val1.ival = 0; val1.s = string("Anna")
Data val1 = { 0, "Anna" };
//初始值顺序必须与声明顺序一致,初始值列表的元素个数可以少于类成员数量,优先初始化靠前的元素,但不能多。
23、字面值常量类
24、类的静态成员
存在的意义:这些成员与类本身有直接关系,但与类的对象没有关联;
声明方式:成员声明前加上关键字static。可以是public或private,类型可以是常量、引用、指针、类类型等。
不与任何对象绑定,故没有this指针,进而静态成员函数不能声明成const的,函数体内不能使用this指针。
double r;
r = Account::rate(); //使用作用域运算符访问静态成员
//也可使用类的对象、引用或指针来访问静态成员
//成员函数不用通过作用域运算符就能直接使用静态成员
静态成员的关键字static只出现在类内部的声明语句,静态成员函数的定义和其他成员函数一样。
静态数据成员不属于类的任何对象,也就不是创建类的对象时被定义初始化的,也就不是由类的构造函数初始化的。必须在类外定义和初始化每个静态成员,且只能定义一次。类似全局变量,一旦被定义,将存在于程序的整个生命周期中。
double Account::interestRate = initRate(); //定义并初始化一个静态成员,声明在类内部
25、静态成员的类内初始化:通常也应该在类的外部定义一下该成员
//类内部
static constexpr int period = 30; //period 是常量表达式
double daily_tbl[period];
//类外部
constexpr int Account::period; //一个不带初始值的静态成员的定义,初始值在类的定义内提供
26、静态成员能用于某些场景,而普通成员不能
class Bar{
//静态数据成员可以时不完全类型
static Bar mem1; //正确
Bar *mem2; // 正确
Bar mem3; //错误
//静态成员可以作为默认实参
Bar& clear(char = bk);
static const char bk;
};