C++ Primer 4th 要点总结

来源:互联网 发布:ins社交软件全名 编辑:程序博客网 时间:2024/06/08 12:19

第12章

1.   类内部定义的函数默认位inline,显示指定inline成员函数,inline可以出现在声明中或定义中,也可以两者都出现;不在类定义体内定义的inline成员函数,定义通常放在类定的头文件中


2.  前向声明是一个不完全类型,例如

class Example;
不完全类型可以用作指向该类型的指针或引用,或声明(不是定义)使用该类型作为形参或返回值的函数,不能被继承

类不能有自身成员,但是可以有指向自身的引用或指针成员


3.  const成员函数不能改变其所操作的对象的数据成员

const必须同时出现在定义和声明处

非const成员函数中,this指针的类型是指向类类型的const指针;const成员函数中,this指针是一个指向const类对象类型的const指针

不能在const成员函数中返回指向类对象的普通引用,const成员函数只能返回*this作为const引用

基于成员函数是否为const可以重载成员函数,基于一个指针形参是否为const,也可以重载函数

const对象只能使用const成员,非const对象能使用任一成员,但是非const版本是一个更好的匹配

class Example{Example& example_fun() {return *this;}const Example& example_fun() const{return *this;}}
如果希望在const成员函数中,仍然可以修改数据成员,将数据成员声明位mutable,mutable成员永远不能为const,即便是const对象的成员


4. 定义类型的成员,使用作用域操作符访问,例如

class Example{public:typedef int example_type;};Example::example_type
在类外部定义的成员函数,形参表和函数体处于类的作用域中,可以引用类的private成员;返回类型不属于类作用域,如果返回类型是类定义的类型,必须使用完全限定名
class Example{private:typedef int type;type example_fun(type, type); };Example::type example_fun(type a, type b) {} //尽管返回类型不在类作用域中,仍然可使用private域定义的类型

5. 类中作用域名字查找的顺序

类定义的两个阶段,编译成员声明;当所有的成员出现之后,才编译其定义本身

类成员声明的名字查找

(1) 检查出现在名字之前的类成员声明

(2) 检查包含类定义的作用域中出现的声明以及出现在类定义之前的声明

class Example{public:type example_fun(type, type); //找不到type,因为type出现在后面typedef int type;};
类成员定义中名字的查找

(1)检查成员函数局部作用域

(2)检查所有的类成员的声明

(3)检查类成员函数定义之前的作用域中的声明

class Example{public:void example_fun(){type a;}typedef int type;};

6. 构造函数不能声明为const

构造函数包含两个阶段: 初始化阶段,显式使用初始化式初始化或编译器隐式初始化;计算阶段,执行构造函数函数体中的语句

初始化阶段中,对于没有显示初始化的成员,类类型成员运行默认构造函数,内置或复合类型成员依赖于对象作用域,全局作用域初始化为0,局部作用域不初始化

没有默认构造函数的类类型成员,const或引用类型的成员,必须显式在初始化列表中初始化

成员初始化的顺序是成员定义的顺序,与初始化列表中的顺序无关

合成的默认构造函数与变量初始化相同的规则初始化成员,类类型调用默认构造函数,普通类型依赖于对象作用域

如果一个类NoDefault具有一个接受string实参的构造函数,没有默认构造函数,具有NoDefault类型成员的类,编译器不会合成构造函数

调用默认构造函数不能加括号,否则认为是函数

class Example{public:};Example exam(); //定义了一个函数,而不是对象
单个实参的构造函数定义了从形参到类类型的隐式转换,使用类类型的地方,可以使用形参类型;将构造函数声明为explicit可以禁止隐式转换,此时可以使用匿名对象
class Example{public:int data;explicit Example(int _d = 0):data(_d) {}};void fun(Example exam);fun(Example(10)); //不能使用隐式转换,但是可以使用临时对象
类成员的显式初始化,要求没有定义构造函数(复制构造函数),并且所有的数据成员均为public,类似于C中的struct使用{}初始化

7. 必须先定义包含成员函数的类,才能将成员函数设为友元

友元声明将类或非成员函数引入到外围作用域中,友元函数可以在类内部定义,该函数的作用域扩展到包含类定义的作用域

class X{friend class Y;friend void f(){do_something;}}class Z{Y *ymem;void g(){return ::f();}}

8. static数据成员和成员函数

static只能出现在类定义体内,不能出现再类定义体之外

static成员函数没有this形参,只能访问static成员,不能直接使用非static成员

static成员函数不能声明为const函数,也不能声明为虚函数

static数据成员必须在类定义体外部定义一次

class X{static int a;}int X::a = 0;
static数据成员在整个继承体系中只有一个
struct Base{static int a;};int X::a = 0;struct Derived1: public Base {};struct Derived2: public Base;{};Base base;Derived1 de1;Derived2 de2;++Base::a;std::cout << de1::a << endl; //输出1std::cout << de2::a << endl; //输出1
const static可以在类定义体内初始化,可以不在类定义体外部定义

static数据成员的类型可以是该成员所属的类类型,非static成员只能是其自身所属类类型的指针或引用

class Bar{static Bar mem1;Bar *mem2;Bar &mem3;};
static数据成员可以用作默认实参

9. 复制初始化调用复制构造函数,首先用指定构造函数构造一个临时对象,然后调用复制构造函数将临时对象复制进正在创建的对象

如果将将复制构造函数声明为explicit,则不能隐式调用复制构造函数,例如不能使用“=”初始化对象,或把对象作为实参传递(除非形参是对象的引用而不是对象本身)

函数参数,返回值,容器和数组元素都需要复制构造函数

合成复制构造函数逐个复制数组成员中的每一个元素

将复制构造函数定义为private,并且没有函数体(只声明不定义),可以防止对象的复制(友元无法复制,因为没有函数体,链接错误)

三法则:如果需要析构函数,那么也需要复制构造函数和赋值操作符,但是如果需要将析构函数定义为虚函数时,三法则不成立

合成的析构函数

编译器总是会合成一个析构函数,即便自己定义了析构函数,按照类中声明次序的逆序撤销成员

10.  继承

派生类只能通过派生类对象访问基类的protected成员,派生类对基类类型的对象的protected成员没有特殊的访问权限

class Base{protected:int a;};class Derived: public Base{void fun(Derived &d, Base &base){int _a = a;   //ok,use this->aint _b = d.a; //okint _c = base.a; //error}}
派生类的虚函数可以返回基类函数所返回类型的派生类的引用或指针(但不能使派生类对象,必须是引用或指针)

虚函数的默认实参静态绑定,即通过基类的指针调用虚函数时,默认实参静态绑定为基类虚函数的实参

公共继承,基类成员保持自己的访问级别;保护继承,public在派生类中变为protected成员;私有继承,基类成员均变为private

在派生类中,可以使用using声明,可以恢复基类成员的访问级别,但是不能使访问级别比基类中原来的访问级别更加宽松或严格

class Base{public:int a;protected:int b;};class Derived: private Base{public:using Base::a; //a是public,可以在用户代码中访问}
友元关系不能继承,例如A是B的友元,A对B的派生类没有特殊访问权限,A的派生类对B没有特殊访问权限

基类定义的静态成员在继承体系中只有一个

用派生类对象初始化基类对象的过程

Derived obj;Base &tmp = obj; //在public继承中,可以在用户代码中将基类的引用或指针绑定到派生类对象,但是非public继承没有这样的权限Base(const Base&); //调用基类的复制构造函数

没有从派生类对象到基类类型对象的直接转换

Base base;Derived *pde = &base; //errorDerived &rde = base; //errorDerived de = base; //error
派生类的构造函数

合成默认构造函数,首先调用基类的默认构造函数,然后按照变量初始化规则初始化派生类成员(与作用域相关)

首先初始化基类,然后按照声明次序初始化派生类成员,并且只能初始化直接基类,例如

class A{};class B:public A{};class C:public B{public:C():A() {} //只能初始化直接基类};
复制构造函数初始化列表中显式初始化基类,否则视为调用基类的默认构造函数,而不是基类的复制构造函数

赋值操作符的函数体中显式调用基类的复制操作符,否则基类部分没有赋值

class Derived:public Base{Derived(const Derived& de):Base(de) {}Derived& operator= (const Derived& de){if (this != &de){Base::operator=(de);}}}
首先调用派生类析构函数,然后按照继承层次依次向上调用各基类的析构函数

构造函数不能为虚函数,如果赋值操作符为虚函数,由于其参数不同(参数为各自类型的引用),会导致定义多个不同的虚函数

0 0