C++常用知识点汇总

一、const,&:

    When:实参与const引用参数不匹配时,编译器将创建临时变量?

        1、实参的类型正确,但不是左值;

        2、实参类型不正确,但可转换为正确类型。

        Why:引用形参尽可能声明为const?

        1、可以避免无意中修改数据;

        2、使函数能够处理const和非const实参;

        3、使用const引用可使函数能够正确生成并使用临时变量。

    函数返回值应避免返回函数终止时不再存在的内存单元引用或指针。

     避免措施:1、返回一个作为参数传递给函数的引用;2、使用new

    按引用传递对象:

     1、提高效率(按值传递,复制构造函数,析构函数)。

     2、在继承使用虚函数时,被定义为接收基类引用参数的函数可以接受派生类。

二、函数重载:

    编译器在检查函数特征标时,将类型引用和类型本身视为同一个特征标。

    函数重载只有特征标不同。

三、函数模板:

    隐式实例化:

    template <typename T>

    void Swap(T &a, T &b){...}

     显式实例化:

    template void Swap<int>(int ,int){...}

     显式具体化:

    template < > void Swap<Job> (Job &j1, Job &j2){...}

    关键字decltype:

    1、 double a = 5.5;     double &b = a; decltype(b) c;      // c : double &

    2、long function(int);  decltype (function(3)) w;     //w: long

    3、decltype ( (a) ) a0 = a;    //a0: double &

    4、int n = 1;     decltype(n+2) n0;        // n0: int

    5、template<class T1, class T2>

       auto function(T1 x, T2 y) -> decltype(x + y){

        ...

        return x + y;

       }

四、定位new运算符:

    #include <new>

    未重载的new运算符工作原理:它返回传递给它的地址,并将其强制转换为void *,以便能够赋给任何指针类型。

    若定位new运算符使用了常规new分配的内存,需要显式地为定位new运算符创建的对象调用析构函数:p->~Test();

    注意上一点调用析构函数的顺序。

五、对象和类:

    转换构造函数:

    接受一个参数的构造函数允许使用赋值语法将值赋给对象,使用explicit可关闭此特性。

    转换函数:

    转换函数必须是类方法,不能有返回类型和参数:operator double(){ return double(id); }

    explicit operator int() const; //隐式不能调用,

    When:调用析构函数:

    1、如果创建的是静态存储类对象,其析构函数在程序结束时自动被调用。

    2、如果创建的是自动存储类对象,其析构函数将在程序执行完代码块自动被调用。

    3、若对象是new创建的,则它将驻留在栈内存或自由存储区中,当使用delete释放内存时,其析构函数自动被调用。

    4、对于临时对象,程序将在结束对该对象的使用时自动调用其析构函数。

    在类声明中定义使用常量的方法:

    1、enum { Months = 12 };

    2、static const int Months = 12;

    作用域内枚举:

    enum class : short egg{Small, ..., Large};    //使用:egg::Small

    运算符重载

    友元函数:

    1、友元函数不是成员函数,不能使用成员运算符来调用;

    2、但与成员函数的访问权限相同;

    3、不要在定义中使用friend;

    4、T1 = T2.operator + ( T3 );        // 成员函数 运算符重载

           T1 = operator + ( T2 + T3 );    // 非成员函数(友元函数),运算符重载

    复制构造函数参数必须为const &

    何时使用复制构造函数?

    1、新建一个对象并将其初始化为同类现有对象时:

    String test1( test0 );

    String test2 = test0;   //也可能调用赋值运算符

    String test3 = String( test0 );    //也可能调用赋值运算符

    String *pTest = new String( test0 );

    2、每当程序生成了对象副本时:函数按值传递对象 或 函数返回对象,

    3、编译器生成临时对象。

    也可能调用赋值运算符

    赋值运算符编写:

    String & String::operator= (const String &st){

    if( this == &st )

    return *this;

    delete [] str;

    len = st.len;

    str = new char [len + 1];

    std::strcpy(str, st.str);

    return *this;

    }

    调用静态成员函数使用作用域解析运算符(类限定符)

    常见的两种返回非const对象的情形:

    1、重载赋值运算符:效率

    2、重载与cout一起使用的<<运算符:必须,返回ostream &;

      不能返回ostream的原因是ostream没有公有的复制构造函数。

    返回对象而不是引用的例子:重载算术运算符(因为引用指向的对象不复存在;需调用复制析构)

    成员初始化列表:

    1、只有构造函数可使用

    2、非静态const类成员和&类成员,必须使用(因为它们只能在被创建时初始化)

六、类继承:

    有两种重要的机制可用于实现多态公有继承:

     1、在派生类中重新定义基类方法;

     2、使用虚方法。

    如果没有使用关键字virtual,程序将根据引用类型或指针类型选择方法;如果使用了virtual,程序将根据引用或

    指针指向的对   象的类型来选择方法。

    如果在派生类中调用基类方法:

     1、若是virtual方法,参照上一点;

     2、类名::方法

    为何需要虚析构函数?

     如果析构函数不是虚的,则将只调用对应于指针类型的析构函数;而对于指针指向的对象的析构函数不会调用。

     使用虚析构函数可以确保正确的析构函数序列被调用。

    将源代码中的函数调用解释为执行特定的函数代码块称为函数名联编。

     静态联编:编译时非虚方法完成联编。

     动态联编:运行时编译器选择正确的虚方法的代码。

    ●虚函数的工作原理:隐藏成员(指针),虚函数表,派生类定义新的虚函数的地址被添加到虚函数表中

    ●使用虚函数的成本:

     1、每个对象都增大,增大量为存储地址的空间;

     2、对于每个类,编译器都创建一个虚函数地址表

     3、对于每个函数调用,都需要执行一项额外的操作,即到表中查找地址

    构造函数、友元函数不能是虚的,析构函数应当是虚函数。

    1、若派生类更改且仅更改虚函数的特征标,这不是重载,它会隐藏基类版本。

     2、返回类型协变

     3、如果基类声明被重载了,则应在派生类中重新定义所有的基类版本;如果只重新定义一个版本,

     其他版本将被隐藏。

    纯虚函数,抽象基类

    基类和派生类都使用动态内存分配时,派生类的析构函数、复制构造函数、赋值运算符都必须使用相应的基类方法来处理

    基类元素:对于析构,自动完成;对于构造,初始化成员列表中调用基类的复制构造函数来完成(若没,会调用默认构造);

    对于赋值,使用作用域解析运算符显式地调用基类的赋值运算符来完成

    如何使用基类的友元函数?

将(友元的)参数强制类型转换成基类

    公有继承中,什么不能被继承?

     构造函数、析构函数、赋值运算符、友元函数

    派生类引用不能自动引用基类对象,如何使其可引用?

     1、转换构造函数:派生类(const 基类 &);

     2、定义一个将基类赋给派生类的赋值运算符;

     3、显式强制类型转换。

/*********************************************************************************************/

    私有继承、包含两者区别:

     1、包含版本提供的是被显式命名的对象成员,私有继承提供的是无名称的子对象成员;

     2、构造函数成员初始化列表中,包含使用“基类对象名(参数)”形式构造基类;私有继承使用“类名(参数)”形式构造基类。

     3、包含使用对象来调用方法,私有继承使用类名::来调用基类方法。

  1.     私有继承如何使用基类对象本身,基类成员、方法?    强制类型转换
  2.     在私有继承中,在不进行显示类型转换的情况下,不能将指向派生类的引用或指针赋给基类引用或指针。
  3.     私有/保护继承中,让基类方法在派生类外面可用,2种方法:

     1、定义一个使用该基类方法的派生类方法;

     2、使用using声明:在类声明public中,using std::valarray<double>::min;

        using声明只适用于继承,不适用于包含。

    多重继承,虚基类,成员初始化列表规则

    类模板:

        1、隐式实例化:

        Array<double, 30> *pt;

        pt = new Array<double 30>;

        2、显式实例化:

        template class Array<string, 100>

        3、显式具体化:

        template <> class SortedArray<const char *>{...}

        4、部分具体化:

        template <没有被具体化的类型参数T1> class Pair<T1, int>{...}

      类模板中表达式参数的限制,eg,ArrayTP<T, int>:

        1、表达式参数可以是整型、枚举、引用、指针:eg中int不能替换为double,但可为double *;

        2、模板代码不能修改参数的值,不能使用参数地址:eg中,不能n++、&n;

        3、实例化模板时,用作表达式参数的值必须是常量表达式。

     模板类的友元:

        1、非模板友元

        2、约束模板友元:模板函数前向声明;类中将模板声明为友元;为友元提供模板定义。

        eg:

        template <typename T> void counts();

        template <typename T> void report(T &);

        template <typename TT>

        class Test{

            ...

        public:

            ...

            friend void counts<TT>();

            friend void report<>(Test<TT> &);

        };

        template <typename T>

        void counts(){ ... }

七、异常:

    throw的工作过程?

    函数调用原理?栈解退?两者区别?

        函数返回仅仅处理该函数放在栈中的对象(一次返回仅处理一个函数放在栈中的对象),

        而throw语句则处理try块和throw之间整个函数调用序列放在栈中的对象(一串)。

        引发异常时编译器总是创建一个临时拷贝,即使异常规范和catch块中指定的是引用。

    <exception>

八、C++标准程序库,STL

九、i/o

十、补充:

    ●链接性为内部的静态变量:

    static int counts;      <->

    namespace{

        int counts;

    }

    ●auto:自动类型推断

    ●类内成员初始化

    ●右值引用&&,移动语义:

        Useless(Useless && f) : n(f.n){   //移动复制构造函数

            ++ct;

            pc = f.pc;

            f.pc = nullptr;  //窃取

            f.n = 0;

        }

        eg: Useless three(one + two);  //若无移动复制构造函数,将调用复制构造函数

     可能:移动赋值运算符和移动复制构造函数的参数不能是const &,因为方法修改了源对象。

     强制移动:#include <utility>

             three = std::move(one);

    ●default可用于禁止编译器使用特定方法,只用于6个特殊的成员函数:function = default;

     delete可用于禁止编译器使用特定方法:function = delete;

    ●委托:在一个构造函数的定义中使用另一个构造函数(成员初始化列表)。

    ●继承构造函数:让派生类能够继承基类构造函数: using BS::BS;

    ●override: 覆盖一个虚函数,将其放在参数列表后面。

     final:禁止派生类覆盖特定的虚方法。

    ●lambda表达式:

        1、count = std::count_if(numbers.begin(), numbers.end(),

                                [](int x){return x %3  == 0;});

        2、仅当lambda表达式完全由一条返回语句组成时,自动类型推断才有用,否则:

            [](double x) -> double{ int y = x; return x -y; }

        3、lambda表达式可访问作用域内的任何动态变量:

            [&]按引用,[=]按值,

    ●模板和函数参数包:

     void show(const T& value){...}  //参数展开后,为避免无穷递归,定义此函数

     template <typename T, typename... Args>

     void show(const T& value, const Args&... args){...}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章