CppPrimer笔记 Chapter7 类
标签(空格分隔): Cpp
this(7.1.2)
- this默认为绑定在非常量对象上的常量指针.那么它不能用于常量类的访问.因此,常量对象,及其指针,引用,都只能调用常量成员函数.
- 借助const成员函数,将this指针隐式修改为 指向常量对象的常量指针.
class Sales_data
{
public:
string isbn1() const {return bookNo;}
string isbn2() {return bookNo;
string isbn;
}
const Sales_data s_c;
Sales_data s;
s_c.isbn1();//ok
s_c.isbn2();//error!
s.isbn2();//ok
- 注意const函数,若返回*this,那么是一个常量引用.
构造函数基础(7.1.4)
- 构造函数不能声明为const的,创建一个const类时,知道构造函数完成初始化过程,对象才能真正取得常量属性
- Q:在构造函数中的this指针类型,特别是常量对象
- A:为普通的
- 默认构造函数:当类没有显示地定义构造函数时
- 如存在类内初始值,则用它来初始化成员
- 否则,默认初始化该成员
- 类中包含一个其他类类型的成员且这个成员的类型没有默认构造函数,则编译器无法初始化该成员.注意,包含的类即使存在自定义的构造函数仍会报错.
- 构造函数初始值列表:被忽略的数据成员以默认构造函数相同的方式隐式初始化,不会是未定义
Sales_data(const string &s) :bookNo(s){}
等价于
Sales_data(const string &s) :
bookNo(s), units_sold(0),revenue(0){}
友元(7.2.1)(7.3.4)
- 类用
friend
作为关键字 位于函数声明的开始,使其他类或者函数访问它的非公有成员.
但这只是给了某个函数一个权限,而非通常意义的函数声明.函数需再次声明,即使这个函数定义在了此处.
因而通常将友元的声明与类本身放置在同一个头文件中(类的外部)
struct X{
friend void f(){/*定义*/}
X(){f();}//error f未被声明
void g();
void h();
};
void X::g(){return f();}//error f未被声明
void f();
void X::h(){return f();} //right
- 友元不具有传递性
内联函数(7.3.1)
- 如果在类体外定义inline函数,则必须将类定义和成员函数的定义都放在同一个头文件中,否则编译时无法进行置换(将函数代码的拷贝嵌入到函数调用点)
可变数据成员(7.3.1)
- 利用
mutable
关键字,使得某个变量永远不会是const. 即使在const对象,或者函数内,其也可以被改变.
类的作用域(7.4)
- 这里的名字查找借助像VA这样的工具,双击该名字便会高亮显示…不用自己背啦….
- 名字查找
- 在名字所在的块中寻找声明,只考虑在名字的使用之前出现的声明
- 若没有找到,继续查找外层作用域
- 若最终未找到,则报错
- 编译器处理完类中的全部声明后才会处理成员函数的定义
typedef double Money;
string bal;
class Account
{
public:
//看见Money,在此域内寻找,未找到,直接跳到外围,发现为doubel
//看见bal,由于 编译器处理完类中的全部声明后才会处理成员函数的定义 ,因而找到Account::bal.
Money balance(){ return bal; }
private:
//找到外围的double Money
Money bal;
};
- 类中成员若使用了外层作用域的某个名字,而该名字代表一种类型,则类不能之后重定义该名字,即使是同样的定义.
- Q: (然而我的vs2013可以….有点迷)
typedef double Money;
class Account
{
public:
Money balance(){ return bal; }
typedef double Money;
private:
Money bal;
};
- A:
- 成员定义中的普通块作用域的名字查找
- 在成员函数内查找改名字的声明,只有在函数使用之前出现的才会被考虑
- 如果在成员函数内未被找到,则类内继续查找,这时类的所有成员都可以被考虑
- 类内也没有找到,在成员函数定义之前的作用域内继续查找
构造函数(7.5)
- 若没有在构造函数的初始值列表中显示地初始化成员,则成员将在函数体之前默认初始化.之后的为正常的赋值操作,因而有些const的类成员此时便不能赋值.
class ConstRef{
public:
ConstRef(int ii);
private:
int i;
cosnt int ci;
int &ri;
};
ConstRef::ConstRef(int ii)
{
i = ii;
ci = ii;//error 不能给const赋值
ri = i;//error ri未被初始化
}
- 成员初始化顺序与它们在类中定义的出现顺序一致,因而初始化列表最好与成员声明顺序保持一致.
class X{
int i;
int j;
public:
X(int val): j(val),i(j){}//error i在j前被初始化
};
- 如果定义了其他构造函数,最好也提供一个默认构造函数
- 能通过一个实参调用的构造函数定义了一条从构造函数的参数类型转向类类型隐式转换的规则.但只允许自动执行一步类型转换.
- 可以通过将构造函数声明为
explicit
加以阻止调用该函数进行的隐式类型转换.
但是只能在类内声明构造函数时使用.
此时也组织了拷贝形式的初始化Sales_data item = string("44");
此时可以转换显示地使用构造函数
item.combine(Sales_data(string("55")));
item.combine(static_cast<Sales_data>(cin));
//如类 Sales_data 有只接受string 与 cin 的构造函数
stirng null_book = "55";
item.combine(null_bool);//string->Sales_data
item.combine(cin);//cin->Sales_data
item.combine("55");//error 两步类型转换
item.combine(string("55"));//ok
- 聚合类可以用花括号直接初始化,并且初始值的顺序必须与声明的顺序一致.以下为其条件(不重要)
- 所有成员都是
public
- 没有定义任何构造函数
- 没有类内初始值
- 没有基类,也没有virtual函数
- 所有成员都是
- 字面值常量类(不重要)
类的静态成员(7.6)
- 利用
static
声明类的静态成员. 外部定义时不能重复关键字,关键字只出现在类内部的声明语句 - 在我看来,理解成为属于某个类的类全局变量.因而
- 静态成员函数不能声明为const,也不能使用this
- 可以用 类名+
::
来直接访问 - 必须在类的外部定义和初始化,且只能定义一次
- 一旦定义,生命周期为整个程序
- 静态成员可以提供const整数类型的类内初始化,不过要求静态成员是constexpr. 即使如此,通常情况下也要在类的外部定义一下该成员.
- 静态成员可以为不完全类型,并可以当做默认实参.(不完全类型指类在声明之后,定义之前)
class Bar{
static Bar mem1; //ok
Bar *mem2; //ok
Bar mem3l //error 我理解为这里不知道要分配多大的内存空间,且递归后为无穷,所以禁止