《c++ primer》学习笔记HB

0725

  • 局部对象
  • 自动对象:只有当定义它的函数被调用时才存在的对象。
  • 静态局部对象:定义静态的函数结束时,静态不会被撤销,在函数多次调用过程中,静态局部对象会持续存在并保持它的值,在程序结束前都不会被撤销。
  • 内联函数
  • 在函数返回类型前加上关键字inline,将它在程序中每个调用点上内联地展开,避免函数调用开销,适用于优化小,只有几行且经常被调用的函数。大多数编译器不支持递归的内联,应该在头文件中定义。缺点:增加编译后的代码量,空间开销。
  • 类的成员函数
  • 类所有成员必须在花括号内声明,但成员函数可在类外定义,编译器隐式将在类内定义的成员函数当作内联函数。
  • 形参this初始化为调用函数对象的地址。
  • 加了const的函数称为常量成员函数,改变了隐含this形参的类型,const成员函数不能修改调用该函数的对象。 

    double function() const;

  • this->isbn 等同于(*this).isbn;
  • 类外定义成员函数:double 类名::函数名( )
  • 构造函数
  • 通过构造函数来初始化数据成员,与类同名,无返回类型,一个类可有多个构造函数,每个构造函数必须要与其他构造函数不同数目或类型的形参。
  • 通常应确保每个数据成员都进行了初始化。
  • 构造函数可在类中或类外定义,在类中是在public中定义。
  • 构造函数的初始化列表为类的一个或多个数据成员指定初值。它跟在形参表之后以冒号开头。构造函数的初始化式是一系列成员名,每个成员后面是括在圆括号中的初始值。多个成员的初始化用逗号分隔。
    类名():成员1(0),成员2(0) {}
  • ——函数重载
  • 函数不能仅仅基于不同的返回类型而实现重载。
  • 在函数中局部声明的名字将屏蔽而不是重载在外层作用域中声明的同名函数,故每一版本的重载函数应在同一作用域内声明。
  • 重载确认:
    1. 选择候选函数:与调用函数同名,在调用点上声明可见。
    2. 选择可行函数:形参个数与实名相同;实参与形参类型匹配或可转化(注意默认实参)。
    3. 寻找最佳匹配:原则,实参类型与形参类型越接近匹配越佳。
    4. 含有多个形参的重载确认:其每个实参的匹配优于其他可行函数提供的匹配;至少有一个实参的匹配优于其他可行函数提供的匹配。

0730

  • 实参类型转换:1,精确匹配 2,类型提升 3,标准转换 4,类类型转换。
  • 重载const形参:仅当形参是引用或指针时,形参是否为const才有影响。
  • 非const对const与非const函数都可行。
  • 不能基于指针本身是否为const来实现函数重载,f(int *const),const用于修饰指针本身,而不是修饰指针所指向的类型。
  • ——函数指针
  • 函数指针是指向函数而非指向对象的指针。
  • *的优先级低于 “( )”。
    bool (*pf)(const string&, const string&)
  • 在引用函数名但又没有调用该函数时,函数名将被自动解释为指向函数的指针。
  • 函数指针只能通过同类型的函数或函数指针或0值常量表达式进行初始化或者赋值。
  • 初始化为0时不指向任何函数。指向不同函数类型指针间不存在类型转换。
  • 函数指针调用指向的函数不需要使用解引用操作符。
  • 函数指针做形参
    1.void ffc bool (const string& ,const string &)
    2.void ffc bool (*) (const string&, const string&)
  • 返回指向函数的指针 ,ff(int)返回int(*)(int*, int)
    int (* ff(int))(int*, int);

            等价于

    typedef int (*PF) (int *, int);
    PF ff(int);
    • 具有函数类型的形参会自动转化为指针,而返回类型的函数则不行,故函数可作为形参,不能为返回类型,只能返回函数指针。
    • 指向重载函数的指针类型必须与重载函数的一个版本精确匹配。
    • 刷新缓冲区的操作
    1. 程序正常结束
    2. 缓冲区存满
    3. 用操作符显示刷新,如endl,ends,flush
    4. 在每次输出操作执行完后,用unitbuf操作符设置流的内部状态,从而清空缓冲区
      count << unitbuf << " " << nounitbuf
    5. 可将输出流与输入流关联起来(tie),在这种情况下,在读输入流时将刷新其关联的输出缓冲区,输入操作符读取的是有类型的值,因此读入的对象类型必须和读入值的类型一致。
      cin.tie(&cout) ;//绑定
      cin.tie(0); //解除

    0804

    • ——类
    • public:定义的成员可以被类所有代码访问。
    • private: 定义的成员可被其他类成员访问。
    • protected: 允许派生类访问但仍禁止其他用户访问。
    • 所有类成员必须在类的内部声明,一旦定义完成就无其他办法增加成员了。
    • 在类内部定义的函数默认为inline函数。
    • 数据抽象:使用该类型的程序猿无需知道类型具体实现,仅知道接口即可使用。
    • 程序所有部分都可访问public,其成员定义了类型的数据抽象视图。
    • 使用类的代码不可访问private成员,其封装了类型的实现细节。
    • 数据抽象封装优点:避免内部出现无益的,可能破坏对象的用户级错误;随时间退役可以根据需求改变或缺陷报告来完善类实现,而无须改变用户级代码。
    • 类成员函数可重载,两个重载成员的形参和类型不能完全相同。
    • inline成员函数的定义必须在调用该函数的每个源文件中是可见的。
    • 定义一个新类型时不进行存储分配,定义类型一个对象时进行分配。
    • 不能从const成员函数返回指向类对象的普通引用。const成员函数只能返回*this作为一个const引用。
    • 可变数据成员:将数据成员声明为mutable变成可变数据成员,const函数也可修改其值。
    • 定义类型的成员,如screen::index,使用作用于操作符来访问。
    • 如果函数在类定义体之外定义,则返回类型的名字在类作用域之外,如果返回类型使用类定义的类型,则必须使用完全限定名。
    • 类成员中的名字查找:1)检查成员函数局部作用域2)检查对所有类成员的声明3)检查在此成员函数定义之前的作用域中出现的声明。
    • 构造函数不能声明为const,const构造函数是没必要的,构造函数的工作是初始化对象。
    • 构造函数可以定义在类的内部或者外部,构造函数初始化列表只在构造函数的定义中而不是声明中指定。
    • 省略初始化列表并在构造函数的函数体内对数据成员赋值是合法的。
    • 使用构造函数初始化列表是初始化数据成员,而在构造函数内操作时是对数据成员进行赋值。
    • 内置或复合类型的成员初始化依赖对象作用域,在局部作用域不初始化,全局时初始化为0.
    • 设有默认构造函数的类类型成员,const或引用类型成员,都在初始化列表中进行初始化。
    • 可以初始化const对象或引用类型对象,但不能对它们赋值。
    • 初始化列表中成员被初始化的次序就是定义成员的次序。
    • 按照与成员声明一致的次序编写构造函数的初始化列表是个好主意,此外,尽量避免使用成员函数来初始化其他成员。
    • 当构造函数呗声明为explicit时,编译器将不使用它作为转换操作符。普通构造函数能够被隐式调用,而explicit构造函数只能被显示调用。
    • explicit只用于类内部的构造声明上,外部定义不再重复。
    • ——友元
    • 友元允许一个类将对其非公有成员的访问权授予指定的函数或类,以关键字friend开始,必须先定义包含成员函数的类才能将其成员函数设为友元,另一方面,不必预先声明类和非成员函数来将它们设为友元。
    • ——类静态成员
    • 优点:1)static成员的名字在类的作用域中,可避免与其他类成员或全局对象名字冲突;2)可以实施封装,static可以为私有成员,而全局对象不可以;3)可容易看出static成员是与特定类关联的,可清晰显示程序猿意思。
    • static函数无this指针,不能声明为虚函数。
    • static数据成员类型可以是该成员所属的类类型,而非static只能为指向类的指针或引用。
    • static数据成员可以为默认实参,而非static不可。
    • 为了防止复制,类必须显示声明其复制构造函数为private。

    0805

    • ——赋值操作符
    • 重载操作符:operator后跟着所定义操作符的符号,有一个返回值和形参表。形参表必须具有与该操作符操作数书目相同的形参。(如操作符是一个成员,则包括隐式this形参)。当操作符为成员函数时,它的第一个操作数隐式绑定到this指针。
    • 赋值操作符返回类型应该与内置类型赋值运算返回类型相同。
    • 一般而言,如果类需要复制构造函数,它也会需要赋值操作符。
    • ——析构函数
    • 当对象的引用或指针超出作用域时,不会运行析构,只有删除指向动态分配对象指针或实际对象超出作用域时,才运行。
    • 容器中的元素总是按逆序撤销。
    • 析构函数通常用于释放在构造函数或在对象生命期内获取的资源。
    • 如果需要析构函数,则它也需要赋值操作符和复制构造函数。
    • 编译器总会合成一个析构函数,按对象创建时的逆序撤销每个非static成员,析构函数是个成员函数,它的名字是在类名字前加一个~,无返回值,无形参,不能重载,只能提供一个析构函数。
    • 即便编写了析构函数,系统合成的析构函数仍然运行。
    • ——指针成员
    • 复制时指针会纠缠在一起,释放一个另一个容易出现悬垂指针。
    • 1)使用只能指针,增加一个计数类,计数类为private,加上类为友元。计数类为公用部分,复制式赋值时,计数加一,析构时减一,可防止悬垂指针,要改变指针的值时,通过计数类来改。
    • 2)使用定义值型类,复制构造式赋值构造时,复制指针的值,而不是指针,每一次都创建一个副本,指针指向这个副本。
    • ——重载操作符
      sales_items operator+(const sales_items&, const sales_items& )
    • 不能重载的操作符:"::", ".*", ".","? :"。
    • 通过连接合法符号可以创建新的操作符,如“**”.
    • 用于内置类型的操作符其含义不能改变,也不能重定义,故重载操作符必须具有至少一个类类型或枚举类型的操作数。
    • 操作符的优先级、结合性或操作数书目不能改变,除了函数调用operator()之外,用重载操作符时使用默认实参是非法的。
    • 重载操作符不再具备短路求值特性。(T && T,前面为真时,还得计算后面的)。
    • 作为类成员的重载函数,其形参看起来比操作数数目少1,作为成员函数的操作符有一个隐含的this形参,限定为第一个操作数。
    • 一般讲算术和关系操作符定义为非成员函数,而将赋值操作符定义为成员。
    • 操作符定义为非成员函数时,通常将它们设置为所操作类的友元。
    • 重载操作符设计
      • 不要重载具有内置含义的操作符
      • 大多数操作符对类对象没有意义
      • 复合操作符(有单个,也可以定义复合)
      • 相等与关系操作符(定义相应的操作符)
      • 选择成员与非成员
        • 赋值(=)、下标([])、调用(())和成员调用箭头(->)等操作符必须定义为成员。
          • 复合赋值操作符通常应定义为类的成员
          • 改变对象状态或与给定类型紧密结合的其他一些操作符,如自增,自减和解引用通常应定义为类成员。
          • 对称的操作符,如算术操作符、相等的操作符、关系操作符和位操作符,最好定义为普通非成员函数。
    • 输出操作符的重载<<
    • 操作符应接受ostream&作为第一个形参,对类类型const对象的引用作为第二个形参,并返回对ostream形参的引用。
    • 一般而言,输出操作符应输出对象的内容,进行最小限度的格式化,它们不应该输出换行符
    • 定义输入输出操作符时,必须使它成为非成员操作符。
    • 输入操作符>>的重载
    • 输入输出操作符区别:输入操作符必须处理错误与文件结束的可能性。
    • 检查输入流是否有定义来确定是否输入错误。
    • 设计输入操作符时,如果可能,要确定错误恢复措施。如果类输入失败,可将整个对象复位,用默认构造函数创建一个新的类。
    • 算术、关系、操作符
    • 加法返回一个右值而不是引用。
    • 既定义了算术操作符又定义了相关复合赋值操作符的类,一般应定义用复合赋值实现算术操作符。
    • 关联容器及某些算法,使用默认“<”操作符。
    • 一般而言,赋值操作符与复合赋值操作符应返回左操作数的引用。
    • 可以从容器中检索单个元素的容器类,一般会定义下标操作符,即oprator[]。
    • 成员访问操作符
    • 为了支持指针类,c++允许重载引用操作符(*)和箭头操作符(->)。
    • 重载箭头操作符必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象。
      • 如果返回类型是指针,则内置箭头操作符可用于该指针,编译器对该指针解引用并从结果对象获取指定成员
      • 如果返回类型是类类型的其他对象(或是这种对象的引用),则递归应用该操作符,直到返回对象所属类型有成员定义。
    • 自增自减操作符
    • 区别前后缀:后缀式操作符函数接受一个额外的(无用)int型形参。
    • 为了与内置操作符一致,后缀式操作符应返回旧值,并且作为值返回而不是引用。
    • 实现后缀时,先保存一个副本,再作增减操作,再返回副本的值。
    • 调用后缀时加一个int的参数以用来区分。
    • 调用操作符与函数对象
    • 函数调用操作符必须声明为成员函数,一个类可以定义函数调用操作符的多个版本,由形参的数目或类型加以区别。
    • 函数对象:定义了调用操作符的类,即行为类似函数的对象。

    0806

    • 标准库中定义了一组算术、关系与逻辑函数对象类
    • 标准库有一组函数适配器,用于转化和扩展一元、二元函数对象,分为绑定器与求反器。
    • 转换与类类型
    • 类类型转换可支持混合类型表达式,减少所需要操作符的数目。
    • 转换操作符是一种特殊的成员函数,它定义将类类型值转变为其他类型值的转换。
    • 它在类定义体内声明,在operator后接转换类型,如:
      operator int() const{ return val; }
    • 对任何可作为函数返回类型可以转换为指针类型与引用类型。
    • 转换函数必须是成员函数,不能指定返回类型,形参表必须为空。
    • 每个转换函数必须显式返回一个指定类型的值。
    • 类类型转换之后不能再跟另一个类类型转换。(可单步转换,多步转换就出错)
    • 标准转换可放在类类型转换之前。(如small int 类可调用构造函数,即 smallint(int),即可将f(int)->f(smallint))
    • 面向对象编程
    • 三个基本概念:数据抽象,继承,动态绑定。
    • 多态性:通过继承而相关联的类型为多态类型,在许多情况下可以互换地使用派生类型或基态类型的许多形态,在c++中,多态性仅用于通过继承而相关联的类型的引用或指针。
    • 在c++中,基类必须指出希望派生类定义哪些函数,定义为virtual的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数。
    • 在c++中通过基类引用(指针)调用虚函数时,发生动态绑定,引用(指针)既可以指向积累对象,也可指向派生类对象,这一事实是动态绑定的关键,用引用(指针)调用虚函数在运行时确定,被调用的函数是引用(指针)所指对象的实际类型所定义的。
    • 继承层次的根类一般都要定义虚析构函数。
    • 基类通常应将派生类需要重定义的任意函数定义为虚函数。
    • 除构造函数外,任意非static成员函数都可以是虚函数。
    • virtual保留字只在类内部的成员函数声明中出现,不能用在类定义体外部出现的函数定义上。
    • 用户代码可访问public,不可访问private和protected。
    • 派生类可访问基类的public,protected,不可访问private。
    • 类成员和友员即可访问public,也可访问private。

    0807

    • 派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊的访问权限。
      • Item_base为基类,Bulk_item为派生类,Bulk_item定义了一个函数
      • void Bulk_item::memfcn(const Bulk_item &d, const Item_base &b);
      • 该函数可访问自己对象的protected成员及Bulk_item形参的protected成员,但是它不能访问Item_base形参的protected成员。
    • 定义类充当基类时,接口函数应该是public,而数据一般不应为public,被继承的类必须决定实现的哪些部分声明为protected而哪些部分声明为private,希望禁止派生类访问的成员应该设为private,提供派生类实现所需操作或数据的成员应设为protected。
    • 派生类
    • 派生类用类派生列表指定基类,可以指定一个或多个,class class-name:access-lable base-clss。
    • 派生类一般会重定义所继承的虚函数,若没重定义,则使用基类版本。
    • 派生类中虚函数的声明必须与基类中定义方式完全匹配,只有一个例外;若返回对基类型的引用(或指针)的虚函数。派生类中虚函数可返回基类函数返回类型的派生类引用(指针)。
    • 一旦在基类中声明为虚函数,就一直为虚函数,派生类重定义时,可用virtual保留字也可不用。
    • 如果需要声明一个派生类,则声明包含类名但不包含派生列表。
    • 虚函数
    • 通过基类类型的引用或指针进行函数调用,调用指定为虚函数的成员函数时才发生动态绑定。
    • 静态类型:在编译时可知的引用类型或指针类型。
    • 动态类型:指针或引用所绑定的对象的类型仅在运行时才知道。
    • 调用虚函数时,只有在运行时才知道调用哪个版本,只有在这种情况下,直到运行时才知道动态类型。
    • 使用作用域操作符可覆盖虚函数机制并强制函数调用使用虚函数的特定版本
    • 公有、私有、受保护的继承
      • 公有继承:基类成员保持自己的访问级别:基类public为派生的public,protected也一致。
      • 受保护继承:基类public与protected在派生中均为protected
      • 私有继承:基类所有成员在派生类中为private
    • 定义一个类作为另一个类的公用派生类时,派生类应反映与基类的“是一种 is a” 关系
    • 类型之间另一种常见的关系是称为“有一个(Has a)”的关系
    • 派生类可恢复继承成员访问级别,但不能使访问级别比基类中指定的更严格或宽松。
    • 增加一个using声明,如在public下加一个using Base::size,恢复size访问级别
    • 使用class保留字定义的派生类默认private继承,而struct默认public继承。
    原文地址:https://www.cnblogs.com/blueswitkey/p/2734456.html