1:成员函数的声明必须在类的内部,但是它的定义既可以在类的内部,也可以在类的外部。
2:this成员函数通过一个名为this的指针的额外的隐式的参数来访问调用它的那个对象。例如调用一个成员函数的时候,用请求该函数的对象地址初始化this
,例如,如果调用f.func()
,编译器负责将f的地址传给func的隐参数
。func本来没有参数,但是现在就相当于func(&f)
。
3:常量成员函数:
std::string Sales_data::isbn() const
{ return bookNo; }
实际上它就相当
std::string Sales_data::isbn(const Sales_data *const this)
{ return this->isbn; }
表明this是指向常量的指针,所以成员函数不能改变调用它的对象的内容。isbn可以读取它的对象的数据成员,但不可以写入新值。
4:成员函数体可以随意使用定义在类中的成员而无须在意这些成员出现的次序。
5:在类的外部定义成员函数
- 参数,函数名与返回值类型都必须与类中的定义的保持一致。
- 如果是常量成员函数,那么它的定义也必须在参数列表后明确的指明const属性。
- 当此成员函数使用变量名与类中定义的变量一致时,默认使用类中的变量。
6:类相关的非成员函数注意点:如果需要将其成员改变就定义形参类型为普通的引用,要是不改变就定义成const的引用。
7:构造函数
默认构造函数:由编译器为我们生成的,利用类内初始值的数值为我们提供初始化。
构造函数初始值列表:位于:
和{}
之间的部分。
将构造函数一般放到类的内部。
8:在很多我们需要使用动态分配内存的情况下,使用vector
或者string
的类能避免分配和释放内存带来的复杂性。
9:访问说明符:一个c++类中可以有0个或者多个访问符。而且对于某个访问符能出现多少次也没有限制,规定一个访问符限制的范围是从一个访问符开始到遇到下一个访问符号或者整个类结束。
public
:说明符之后的成员可以在整个程序内访问。
private
:说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,即隐藏了细节。
10:struct和class的唯一区别:默认权限的不同
我们可以在类中第一个访问说明符之前定义类的成员,如果是struct
则默认访问权限是public
,如果是class
则默认访问权限是private
。
11:类中的成员函数默认都是内联的,但是我们也可以在类的外部用inline
关键字修饰函数的定义。
12:重载成员函数:成员函数和非成员函数一样都是可以重载的。根据参数的匹配进行。
13:mutable
我们有时需要修改一个成员变量,即使在const的对象的成员。例如
class Screen{
public:
void some_member() const;
private:
mutable size_t access_ctr;
};
void Screen::some_member() const
{
++access_ctr; //尽管这个成员函数是const的,但是access_ctr仍然可以加。因为一个被mutable修饰的可变数据成员永远不可能是const。
}
14:一个const的成员函数如果以引用的形式返回*this,那么它的返回值类型将是一个常量引用。
15:类是一种类型,我们有时候需要在一个类(A)中使用另一个类(B),这就需要将这个类(B)声明在(A)的前面,类的声明我们可以class B;
16:友元、友元类和友元函数:
友元:能够让我们的
非类中成员
访问私有数据域。对于友元函数,我们必须在类的外部对其进行声明,一般位置集中放到类的开始之前。
友元类和友元函数:
class Screen {
friend class Window_mgr; //window_mgr中的函数可以访问Screen中的私有成员。
//但是如果我们只想将成员函数作为友元,我们必须明确指明该成员函数属于哪个类。
friend void Window_mgr::clean(index); //clean()函数也可以访问Screen中的私有成员.
};
如果一个类中包含友元类,它又被其他的类友元,这样并不能说明其他两个类也是友元的,即每个类指负责自己的友元类或者友元函数。
17:委托构造函数
需要注意的是委托构造函数都会先去执行被委托的部分,即最长的那个构造函数。如果构造函数中有函数体,程序会函数体依次执行完成。
18:隐式的类类型的转换
对于只接受一个实参的构造函数,可以定义一条从构造函数的参数类型向类类型隐式转换的规则。
class Sales_data {
public:
Sales_data(const std::string &s,unsigned p,double d):bookNo(s),units_sold(p),revenue(d*p) { }
Sales_data(const std::string &str):Sales_data(str,0,0) { }
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
int main(int argc,char *argv[])
{
std::string null_book = "9-99-999-9999";
item.combine(null_book); //编译器会自动给string创建一个bookno为"9-99-999-9999",units_sold为0,revenue为0的Sales_data。
//但是这种类型转换是只有对一个实参的类型。
}
我们可以用explicit
来抑制构造函数的这种隐式转换,就是在构造函数的前面声明explicit
,它只对一个实参的构造函数有效果,并且只能在类内部声明的时候使用,在外部定义的时候不应该重复。用了explicit
之后,我们只能使用直接初始化。
19:聚合类
- 所有成员都是public
的
- 没有定义任何的构造函数
- 没有类内初始值
- 没有基类,没有virtual
函数
20:字面值常量类:数据成员都是字面值类型的聚合类
- 数据成员必须都是字面值常量
- 类必须至少含有一个constexpr构造函数
22:static
- static修饰符可以保证许多类共享一个变量,类的静态成员存在与任何对象之外.
- 对象中不包含任何与静态数据成员有关的数据。
- 静态数据成员也不与任何对象绑定在一起,它们不包含this指针。
- 只出现在类内部的声明中。
- 即使一个常量静态数据成员在类内部被初始化了,通常情况下也应该在类的外部定义。
class Account {
public:
void calculate() {amount += amount * interestRate;}
static double rate() { return interestRate;} //静态的,只与这整个类有关
static void rate(double);
private:
std::string owner;
double amount;
static double interestRate;
};