C++备忘录

本文详细介绍了C++中的高级编程概念,包括拷贝构造函数、析构函数的虚拟性、右值引用、成员变量的拷贝与赋值、友元、指针数组、类的静态成员、内联静态变量、多态、虚继承、模板特化、操作符重载等,并提供了相应的代码示例和解析。
摘要由CSDN通过智能技术生成

C++备忘录

|C++20高级编程第五版代码下载|

1.C++20高级编程第五版PDF示例代码下载
2.C++20高级编程第五版PDF作业练习代码下载

1.Summary of Compiler-Generated Constructors (C++20高级编程)

在这里插入图片描述

2.销毁对象的顺序和栈上对象的初始化顺序相反,成员对象也是一样的(C++20高级编程)

在这里插入图片描述

3. C++拷贝构造函数只会发生在初始化对象的时候(C++20高级编程)

在这里插入图片描述

4. C++11开始如果你自定义一个拷贝构造函数或者拷贝复制函数,则系统不会默认生成拷贝构造和拷贝复制函数 (C++20高级编程)貌似这个规则被无视了,在VSCode C++11标准编译运行没有任何问题:

在这里插入图片描述

5. 成员变量的拷贝构造和拷贝赋值函数的调用情况(C++20高级编程)

在这里插入图片描述

6. 友元,类,类成员函数,全局函数(C++20高级编程)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7. 指针数组,数组指针和双重指针

    //符号优先级:() > [](从左到右结合) > *(从右到左结合) 比如int(*a)[2]
    //()优先级高,所以代表a是一个指针,然后是[],代表这个指针指向一个数组,int表示数组的元素类型
    //即代表a是数组指针,指向int[2]
    int array[2][2] = {0};
    int(*pArray)[2] = &array[0];//数组指针,&array[0]不是代表元素的双重指针,
                                //虽然array[0]代表的是int*,但是&array[0]不代表双重指针
    int array1[2] = {0};
    int(*pArray1)[2] = &array1;//数组指针
    int *pInt = new int[2];//new 返回的都是地址,和数组声明不一样
    int **pArray2 = new int *[2];//所以双重指针就可以用来表示二维数组
    pArray2[0] = new int[3];
    int(*pArray)[2] = new int[2][2]; //new int[2][2]返回的int(*)[2]而不是int* ,需要注意下!!

8. 右值和右值引用简单使用

void func4(int &&rInt)
{
    rInt = 9; // 此时rInt其实是个左值了
              // 右值引用和左值引用都是左值!!!
              // 右值引用可扩展右值的生命周期和用于移动语义操作
    // int *p = &std::move(rInt); //error! 右值不可以取地址
    int *pInt = &rInt;
    func5(std::move(rInt)); // move将左值转化成右值引用从而触发移动语义,
                            // 实现资源的高效转移而不进行深层的复制
                            // 被move后对象,不应该使用,因为可能造成未定义的行为
}
void func5(int &&rInt)
{
}

9. 如果只是显示自定义了移动函数(move constructor,move assignment operator),拷贝函数(copy constructor,copy assignment operator)均被默认为delete。并且编译器默认实现的move函数,并不会把指针类型的变量置为null,所以如果要用到move函数,需要自己实现最好

10. 如果需要调用的是move函数但是没有自定义它,则会调用copy函数代替,比如test20230605函数里面的情况

class Pear
{
public:
    int price = 0;
    int *pInt = nullptr;
    Sun1 *sun;
    Pear()
    {
        Util::LOGI("Pear()!");
    }
    Pear(int price)
    {
        this->price = price;
        Util::LOGI("Pear(int price)!");
    }
    Pear &operator=(const Pear &) // copy assignment operator
    {
        Util::LOGI("Pear &operator=(const Pear &)!");
        return *this;
    }
    Pear(const Pear &) // copy constructor
    {
        Util::LOGI("Pear(const Pear &)!");
    }
    Pear(Pear &&) // move constructor
    {
        Util::LOGI("Pear(Pear&&)!");
    }
    Pear &operator=(Pear &&src) // move assignment operator > copy assignment operator
    {
        this->sun = src.sun;
        src.sun = nullptr;
        Util::LOGI("Pear &operator=(Pear &&src)! this price:%d,src price:%d", this->price, src.price);
        return *this;
    }

    ~Pear()
    {
        Util::LOGI(" ~Pear()! price--->%d", price);
    }
};

void test20230605()
{
    Pear A, B;
    Pear &&C = Pear{};
    A = C;                      // 调用copy函数
    B = Pear{};                 // 如果有move assignment operator函数就调用move函数,如果没有就调用copy assignment operator函数
    Pear D = std::move(Pear{}); // 如果有move函数,则调用move构造函数,如果没得即调用copy构造函数
}

11.类的static方法成员,const方法成员的基本描述(C++20高级编程)

在这里插入图片描述
const函数(非const成员变量可以调用const和非const成员函数,const成员变量只能调用const成员函数,const成员函数只能调用const成员函数,因为const成员函数里面声明的是const成员变量):
在这里插入图片描述

12.mutable关键字(C++20高级编程)

在这里插入图片描述

class MutableStudy
{
public:
    mutable int counts = 99;//mutable 此关键字标记的成员可以const方法中被改变
    int counts1 = 10;
    void modify() const
    {
        counts++;
        Util::LOGI("void modify() const modify member counts->%d", counts);
        // counts1++; error
    }
};

13.Ref-Qualified Methods,一般的普通方法均可被临时对象和非临时对象调用,但是可通过添加"&,&&"限定符来限制调用(C++20高级编程)

在这里插入图片描述

class RefMethod
{
public:
    int nonTempGetInt() &
    {
        Util::LOGI("int nonTempGetInt() &!");
        return 0;
    }
    int tempGetInt() &&
    {
        Util::LOGI("int tempGetInt() &&!");
        return 0;
    }
};

void test20230606()
{
    RefMethod refMethod;
    refMethod.nonTempGetInt();
    // refMethod.tempGetInt(); error
    std::move(refMethod).tempGetInt();
    RefMethod{}.tempGetInt();
    // std::move(refMethod).nonTempGetInt(); error
}

14.inline关键字:这个关键字只是给编译器一个提示而已,真正决定要不要内联,还是得看编译器的决定(C++20高级编程)

在这里插入图片描述

15.重载时,当用const方法和nonconst方式重载时,const类对象调用const函数,非const类对象调用非const函数,当然非const也可以调用const函数当只有const函数的时候

class ConstOverloadClass
{

public:
    void constOverloadFunc() const
    {
        Util::LOGI("void constOverloadFunc() const!");
    }
    void constOverloadFunc()
    {
        Util::LOGI("void constOverloadFunc()!");
    }
};

void test20230607()
{
    const ConstOverloadClass constConstOverloadClass;
    ConstOverloadClass nonConstconstOverloadClass;
    Util::LOGI("constConstOverloadClass.constOverloadFunc()-->");
    constConstOverloadClass.constOverloadFunc();
    Util::LOGI("nonConstconstOverloadClass.constOverloadFunc()-->");
    nonConstconstOverloadClass.constOverloadFunc();
}

// 打印信息:
// constConstOverloadClass.constOverloadFunc()-->
// void constOverloadFunc() const!
// nonConstconstOverloadClass.constOverloadFunc()-->
// void constOverloadFunc()!

16.类的静态成员必须在外面定义值,静态常量整型可以在类声明处定义值(其他类型可能不行,如float ),常量成员必须显示在类中初始化(直接声明定义,或者构造中赋值),即定义值。所以对待静态成员不管是常量还是非常量都在类外定义即可。

class StaticMemberClass
{
public:
    int nonStaticNumber = 12;
    static int staticNumber;
    const static int constStaticNumber = 89;
    const static int constStaticNumber1;
    // const static float constStaticNumber2=1.0f; // error: 'constexpr' needed for in-class initialization of static data member
                                                    //'const float StaticMemberClass::constStaticNumber2' of non-integral type [-fpermissive]
    const int constNumber;
    StaticMemberClass() : constNumber(0), nonStaticNumber(12)
    {
    }
};
int StaticMemberClass::staticNumber = 345;
const int StaticMemberClass::constStaticNumber1 = 678;
void test20230608()
{
    StaticMemberClass staticMemberClass;
    Util::LOGI("staticMemberClass nonStaticNumber->%d,staticNumber->%d,constStaticNumber->%d,constNumber->%d,constStaticNumber1->%d",
               staticMemberClass.nonStaticNumber,
               StaticMemberClass::staticNumber,
               StaticMemberClass::constStaticNumber,
               staticMemberClass.constNumber,
               StaticMemberClass::constStaticNumber1);
    // 打印信息
    // staticMemberClass nonStaticNumber->12,staticNumber->345,constStaticNumber->89,constNumber->0,constStaticNumber1->678
}

17.C++17开始支持inline static 变量,其好处可以不用在类外去定义他的值,直接在类定义的时候给static变量赋值,当有常量ID变量时,可在copy构造中调用构造函数来初始化ID,或者标记copy构造函数为delete

在这里插入图片描述

class InlineStaticMemberClass
{
public:
    static inline int staticInlineNumber = 19;
    const int number;
    InlineStaticMemberClass() : number{777}
    {
        Util::LOGI("InlineStaticMemberClass() : number{7}!");
    }
    InlineStaticMemberClass(int number) : number(number)
    {
        Util::LOGI("InlineStaticMemberClass(int number)!");
        // this->number = number;
    }
    InlineStaticMemberClass(const InlineStaticMemberClass &) : InlineStaticMemberClass()
    {
        Util::LOGI("InlineStaticMemberClass(const InlineStaticMemberClass &) : InlineStaticMemberClass()!");
    }
};
void test20230609()
{
    InlineStaticMemberClass inlineStaticMemberClass(200);
    Util::LOGI("inlineStaticMemberClass staticInlineNumber->%d,const int number->%d",
               InlineStaticMemberClass::staticInlineNumber, inlineStaticMemberClass.number);
    InlineStaticMemberClass inlineStaticMemberClass1 = inlineStaticMemberClass;
    Util::LOGI("inlineStaticMemberClass1 number->%d", inlineStaticMemberClass1.number);
    // 打印信息:
    // InlineStaticMemberClass(int number)!
    // inlineStaticMemberClass staticInlineNumber->19,const int number->200
    // InlineStaticMemberClass() : number{7}!
    // InlineStaticMemberClass(const InlineStaticMemberClass &) : InlineStaticMemberClass()!
    // inlineStaticMemberClass1 number->777
}

18.有嵌套类时,当内部类为private或者protected时只有外部类里面可以访问,内部类可以访问外部类所有成员,但是外部类只能访问内部类的public成员(C++20高级编程)

在这里插入图片描述

class OuterClass
{
public:
    int OuterClassPublicNumber;
    class InnerClass
    {
    public:
        void test(const OuterClass &outerClass)
        {
            // 内部类可以访问外部类的private protected成员
            int addPrivateAndProtected = outerClass.outerClassPrivateNumber + outerClass.outerClassProtectedNumber;
            Util::LOGI("InnerClass addPrivateAndProtected->%d", addPrivateAndProtected);
        }
        int innerClassPublicNumber;

    private:
        int innerClassPrivateNumber;

    protected:
        int innerClassProtectedNumber;
    };
    void test(const InnerClass &innerClass)
    {
        // 外部类只能访问内部类的public成员
        Util::LOGI("InnerClass innerClassPublicNumber->%d", innerClass.innerClassPublicNumber);
        // innerClass.innerClassPrivateNumber; //error
        // innerClass.innerClassProtectedNumber; //error
        
        //外部类里面可以访问private和protected的内部类
        OuterClass::InnerPrivateClass innerPrivateClass ;   // ok
        OuterClass::InnerProtectedClass innerProtectedClass; // ok
    }

private:
    class InnerPrivateClass
    {
    };
    int outerClassPrivateNumber;

protected:
    class InnerProtectedClass
    {
    };
    int outerClassProtectedNumber;
};

void test20230609()
{
    OuterClass outerClass;
    OuterClass::InnerClass innerClass;
    outerClass.test(innerClass);
    innerClass.test(outerClass);
    // 外部类外面只能访问public声明的内部类
    //  OuterClass::InnerPrivateClass;//error
    //  OuterClass::InnerProtectedClass;//error
}

19.lamda 匿名函数,会自动生成个class,详情如下

int main() {
  auto A =[]()->int{
    int a, b;
    return 0;
  };
  A();
    return 1;
}

上面的代码等价于(用https://cppinsights.io/网站生成的详细代码):

int main()
{
    
  class __lambda_3_11
  {
    public: 
    inline /*constexpr */ int operator()() const
    {
      int a;
      int b;
      return 0;
    }
    
    using retType_3_11 = auto (*)() -> int;
    inline constexpr operator retType_3_11 () const noexcept
    {
      return __invoke;
    };
    
    private: 
    static inline /*constexpr */ int __invoke()
    {
      return __lambda_3_11{}.operator()();
    }
    
    
    public:
    // /*constexpr */ __lambda_3_11() = default;
    
  };
  
  __lambda_3_11 A = __lambda_3_11{};
  A.operator()();
  return 1;
}

20.操作符重载和隐式转换方法重载,测试代码如下

class OverloadOperatorClass
{
public:
    void operator()()
    {
        Util::LOGI("OverloadOperatorClass void operator()()!");
    }
    void operator-()
    {
        Util::LOGI("OverloadOperatorClass void operator-()!");
    }
    operator int() // operator <type> 隐式转换函数重载
    {
        Util::LOGI("OverloadOperatorClass operator int()!");
        return 1;
    }
    operator double() // operator <type> 隐式转换函数重载
    {
        Util::LOGI("OverloadOperatorClass operator double()!");
        return 1.0f;
    }
    operator Apple() // operator <type> 隐式转换函数重载
    {
        Util::LOGI("OverrideOperatorClass operator Apple()!");
        return Apple{};
    }
};

void test20230609()
{
    OverloadOperatorClass overloadOperatorClass;
    overloadOperatorClass.operator()(); // 全名调用
    overloadOperatorClass();            // 省略调用
    overloadOperatorClass.operator-();  // 全名调用
    // overrideOperatorClass-; //error 不能这么调用,可能需要一个参数
    // 打印信息:
    // OverrideOperatorClass void operator()()!
    // OverrideOperatorClass void operator()()!
    // OverrideOperatorClass void operator-()!

    // 隐式调用的常见的几种方式如下:
    Apple apple = overloadOperatorClass.operator Apple(); // operator Apple() :全名称调用
    Apple apple1 = overloadOperatorClass;
    Apple apple2 = static_cast<Apple>(overloadOperatorClass); // 显示转换也会调用隐式转换重载(如果存在)
    int number = overloadOperatorClass;                       // operator int()
    double number1 = overloadOperatorClass;                   // operator double()
    // 打印信息:
    // OverrideOperatorClass operator Apple()!
    // OverrideOperatorClass operator Apple()!
    // OverrideOperatorClass operator Apple()!
    // OverloadOperatorClass operator int()!
    // OverloadOperatorClass operator double()!
}

21.操作符重载参数个数在类中最多有一个,全局的可以最多两个

class GlobalOperatorOverloadClass
{
public:
    void operator+(const GlobalOperatorOverloadClass &r)
    {
        Util::LOGI("void operator+(const GlobalOperatorOverloadClass&r)!");
    }
    void operator+()
    {
        Util::LOGI("void operator+()!");
    }
    // void operator+(const GlobalOperatorOverloadClass &l,const GlobalOperatorOverloadClass &r); //在类内部重载操作运算符只能有<=1的参数个数
};
GlobalOperatorOverloadClass operator-(const GlobalOperatorOverloadClass &l, const GlobalOperatorOverloadClass &r)
{
    Util::LOGI("GlobalOperatorOverloadClass operator+(const GlobalOperatorOverloadClass &l,const GlobalOperatorOverloadClass &r)!");
    return GlobalOperatorOverloadClass{};
}

void test20230612()
{
    GlobalOperatorOverloadClass GlobalOperatorOverloadClass1, GlobalOperatorOverloadClass2, GlobalOperatorOverloadClass3;
    GlobalOperatorOverloadClass1.operator+();
    GlobalOperatorOverloadClass1.operator+(GlobalOperatorOverloadClass2);
    // 全局的operator重载可以最多有2个参数 ,但是在类中定义的操作符重载方法最多只能有一个参数
    GlobalOperatorOverloadClass3 = GlobalOperatorOverloadClass1 - GlobalOperatorOverloadClass2;
    GlobalOperatorOverloadClass1 + GlobalOperatorOverloadClass2;
    // 打印信息:
    // void operator+()!
    // void operator+(const GlobalOperatorOverloadClass&r)!
    // GlobalOperatorOverloadClass operator+(const GlobalOperatorOverloadClass &l,const GlobalOperatorOverloadClass &r)!
    // void operator+(const GlobalOperatorOverloadClass&r)!
}

22.派生类继承于基类的三种方式: private:基类member在派生类中都成private member, protected:基类public member在派生类中都成protected member, public:基类member访问权限在子类中不变. 派生类可以访问基类的public和protected member(protected需要在类内部访问,对象指针无访问权限)(override时可以声明不同的访问权限private->public…).

class BaseClass
{
public:
    virtual void BaseClassVirtualMethod()
    {
        Util::LOGI("virtual void BaseClassVirtualMethod()!");
    }

protected:
    int BaseClassProtectednumber = 1;

private:
    int BaseClassPrivatenumber = 2;

public:
    int BaseClassPublicnumber = 3;
};

class PublicDerivedClass : public BaseClass
{

public:
    void BaseClassVirtualMethod() override
    {
        Util::LOGI("PublicDerivedClass void BaseClassVirtualMethod() override!");
    }
    void showBaseClassMember()
    {
        // BaseClassPrivatenumber; //error 派生类不能访问基类的private member
        // 可以访问protected member,而且只能在类内部,public member没有访问限制
        Util::LOGI("void showBaseClassMember(): BaseClassProtectednumber->%d,BaseClassPublicnumber->%d", BaseClassProtectednumber, BaseClassPublicnumber);
    }
};

class ProtectedDerivedClass : protected BaseClass
{
};

class PrivateDerivedClass : private BaseClass
{
};

void test20230613()
{
    BaseClass baseClass;
    PublicDerivedClass publicDerivedClass;
    publicDerivedClass.showBaseClassMember();
    BaseClass *pBaseClass = &publicDerivedClass;
    pBaseClass->BaseClassVirtualMethod(); // 虽然是BaseClass指针,但是由于多态,BaseClass指针指向的是DerivedClass,所以调用的是DerivedClass重写的方法
    pBaseClass = &baseClass;
    pBaseClass->BaseClassVirtualMethod();
    // 打印信息:
    // void showBaseClassMember(): BaseClassProtectednumber->1,BaseClassPublicnumber->3
    // DerivedClass void BaseClassVirtualMethod() override!
    // virtual void BaseClassVirtualMethod()!

    ProtectedDerivedClass protectedDerivedClass;
    // protectedDerivedClass.BaseClassPublicnumber; // error protected继承,基类的public member在派生类中变为protected member
    // pBaseClass = &protectedDerivedClass; // protected继承 不能通过基类指针指向父类,可能因为基类成员最高访问权限是protected
    PrivateDerivedClass privateDerivedClass;
    // privateDerivedClass.BaseClassPublicnumber; // error private继承,基类的public,protected member在派生类中变为private member
    // pBaseClass = &privateDerivedClass; // private继承 不能通过基类指针指向父类,可能因为基类成员最高访问权限是private
}

23.引用和指针均可有基类类型指向派生类,当派生类转化成基类的时候会丢掉它"额外的数据".(C++20高级编程)

在这里插入图片描述

class BaseClass
{
public:
    virtual void BaseClassVirtualMethod()
    {
        Util::LOGI("virtual void BaseClassVirtualMethod()!");
    }
};

class PublicDerivedClass : public BaseClass
{

public:
    void BaseClassVirtualMethod() override
    {
        Util::LOGI("PublicDerivedClass void BaseClassVirtualMethod() override!");
    }
};

void test20230614()
{
    PublicDerivedClass publicDerivedClass;
    BaseClass &rBaseClass = publicDerivedClass;  // 基类引用refer to 派生类
    BaseClass *pBaseClass = &publicDerivedClass; // 基类指针指向派生类
    rBaseClass.BaseClassVirtualMethod();         // 和指针一样同样可以产生多态行为
    pBaseClass->BaseClassVirtualMethod();
    // 打印信息:
    // PublicDerivedClass void BaseClassVirtualMethod() override!
    // PublicDerivedClass void BaseClassVirtualMethod() override!
}

24.有虚拟函数的类都有个虚拟函数表(只要基类有虚拟函数则派生类就有虚拟函数表),指向具体的virtual方法实现,调用虚拟函数时,找到实际类型对象的虚拟函数表然后调用指向的具体虚拟方法的实现(C++20高级编程).

在这里插入图片描述

25.当一个类可以被继承时,应该把基类的析构函数声明成virtual的,发生多态时才能正确回收内存,基类的析构是virtual的,其派生类析构也是virtual的.

class BaseClass1
{
public:
    BaseClass1()
    {
        Util::LOGI("BaseClass1()!");
    }
    virtual ~BaseClass1()
    {
        Util::LOGI("~BaseClass1()!");
    }
};
class DerivedClass1 : public BaseClass1
{
public:
    DerivedClass1()
    {
        Util::LOGI("DerivedClass1()!");
    }
    ~DerivedClass1()
    {
        Util::LOGI("~DerivedClass1()!");
    }
};
class DerivedClass2 : public DerivedClass1
{
public:
    DerivedClass2()
    {
        Util::LOGI("DerivedClass2()!");
    }
    ~DerivedClass2()
    {
        Util::LOGI("~DerivedClass2()!");
    }
};
void test20230614()
{
    BaseClass1 *pBaseClass1 = new DerivedClass1();
    delete pBaseClass1;
    // 打印信息(当基类的析构函数不是virtual的时候,可以看到发生多态时派生类的析构没有被调用):
    // BaseClass1()!
    // DerivedClass1()!
    // ~BaseClass1()!
    //当一个类可以被继承时应该把析构函数声明成virtual的
    // 打印信息(当基类的析构函数是virtual的时候,可以看到发生多态时,派生类的析构就能被调用):
    // BaseClass1()!
    // DerivedClass1()!
    // ~DerivedClass1()!
    // ~BaseClass1()!
    DerivedClass1 *pDerivedClass1 = new DerivedClass2();
    delete pDerivedClass1;
    // 打印信息(当基类的析构函数是virtual的时候,其子类的析构函数也是virtual的):
    // BaseClass1()!
    // DerivedClass1()!
    // DerivedClass2()!
    // ~DerivedClass2()!
    // ~DerivedClass1()!
    // ~BaseClass1()!
}

26.如果多重继承,最顶层基类有个virtual方法,派生类都实现(除了调用该方法的派生类),如果在派生类中调用该方法,则会从继承关系从下往上找到该方法的实现,而不是直接调用最顶层的基类的方法.

class Book
{
public:
    virtual int getPrice()
    {
        Util::LOGI("Book virtual int getPrice()!");
        return 0;
    }
};
class Math : public Book
{
public:
    int getPrice() override
    {
        Util::LOGI("Math int getPrice() override!");
        return 1;
    }
};

class Calculus : public Math
{
public:
    void buy()
    {
        Util::LOGI("Calculus void buy()!");
        getPrice();
    }
};

void test20230616()
{
    Calculus Calculus1;
    // 如果多重继承,基类有个virtual方法,派生类都实现(除了调用该方法的派生类),
    // 如果在派生类中调用该方法,则会从继承关系从下往上找到该方法的实现,而不是直接调用最顶层的基类的方法
    Calculus1.buy();
    // 打印信息:
    // Calculus void buy()!
    // Math int getPrice() override!
}

27.多重继承的一些概要信息

在这里插入图片描述

28.多重继承当不同的基类有相同的方法,当子类调用此方法时,有两种方法可以避免ambigous.

class Water1
{
public:
    Water1()
    {
        Util::LOGI("Water1()!");
    }
    Water1(const Water1 &)
    {
        Util::LOGI("Water1(const Water1&)!");
    }
};
class Cat
{
public:
    void eat()
    {
        Util::LOGI("Cat void eat()!");
    }
};
class Dog
{
public:
    void eat()
    {
        Util::LOGI("Dog void eat()!");
    }
};
class DogCat : public Cat, public Dog
{
public:
    // using Cat::eat; // using Cat::eat version
    using Dog::eat; // avoid ambigous when invoke DogCat1.eat(). using Dog::eat version
    void eat1()
    {
        eat();      // ambigous when without using 'BaseClass'::eat
        Dog::eat(); // using Dog::eat version
        Cat::eat(); // using Cat::eat version
    }
};

void test20230619()
{
    Water1 Water11 = Water1(); // 构造函数
    Math Math1;
    Math1.Book::getPrice(); // 居然还可以这样调用方法

    // 2 ways to avoid ambigous when two base class both has same method :
    // using XXX::xx or 直接在调用XXX::xx
    DogCat DogCat1;
    DogCat1.eat();
    DogCat1.eat1();
    // 打印信息:
    // Water1()!
    // Book virtual int getPrice()!
    // Dog void eat()!
    // Dog void eat()!
    // Dog void eat()!
    // Cat void eat()!
}

29.虚拟继承可以解决菱形继承的base class ambigous问题,虚基类在多重继承中如菱形继承中只有1个实例化,非基类则有2个.(还有虚表指针和虚基表指针,可多看参考).

虚拟继承,虚基类的概念参考:虚基类内存模型.
一个类调用构造函数的顺序:虚基类->直接基类->类.
类的析构顺序:类->直接基类->虚基类(与构造相反).
类的赋值,拷贝顺序:虚基类->直接基类->类(与构造一致).

class Animal
{
public:
    Animal()
    {
        Util::LOGI("Animal()!");
    }
    Animal(const Animal &)
    {
        Util::LOGI("Animal(const Animal&)!");
    }
    string getName()
    {
        Util::LOGI("Animal string getName()!");
        return "Animal";
    }
};

class Cat : virtual public Animal
{
public:
    Cat()
    {
        Util::LOGI("Cat()!");
    }
    void eat()
    {
        Util::LOGI("Cat void eat()!");
    }
    // string getName()
    // {
    //     Util::LOGI("Cat string getName()!");
    //     return "Cat";
    // }
};
class Dog : virtual public Animal
{
public:
    Dog()
    {
        Util::LOGI("Dog()!");
    }
    void eat()
    {
        Util::LOGI("Dog void eat()!");
    }
};
class DogCat : public Cat, public Dog
{
public:
    DogCat()
    {
        Util::LOGI("DogCat()!");
    }
    // using Cat::eat; // using Cat::eat version
    using Dog::eat; // avoid ambigous when invoke DogCat1.eat(). using Dog::eat version
    void eat1()
    {
        eat();      // ambigous when without using 'BaseClass'::eat
        Dog::eat(); // using Dog::eat version
        Cat::eat(); // using Cat::eat version
        // Dog::getName();
        // InsightC++ 对应上面4个方法:
        // /* static_cast<Dog *>(this)-> */ eat();
        // /* static_cast<Dog *>(this)-> */ eat();
        // /* static_cast<Cat *>(this)-> */ eat();
        // /* static_cast<Animal *>(static_cast<Dog *>(this))-> */ getName();
    }
};

void test20230619()
{
    // Animal Animal1 = Animal(); // 构造函数
    // DogCat DogCat1;
    // DogCat1.Cat::eat(); // 居然还可以这样调用方法

    // 2 ways to avoid ambigous when two base class both has same method :
    // using XXX::xx or 直接在调用XXX::xx
    DogCat DogCat1;
    DogCat1.eat();
    DogCat1.eat1();
    // 打印信息:
    // Animal()!
    // Cat()!
    // Animal()!
    // Dog()!
    // DogCat()!
    // Dog void eat()!
    // Dog void eat()!
    // Dog void eat()!
    // Cat void eat()!

    DogCat DogCat3; // 菱形继承会实例化两个基类
     // error:菱形继承会实例化两个基类所以会产生 ambigous for base class Animal,即使using Dog::getName也同样ambigous,
    // 因为Dog没有getName方法定义也是派生来的,所以他会static_cast<Animal *>(this)->getName()(根据c++ insight);
    // 但是内存模型中有两个Animal的实例,编译器不知道调用哪个Animal的方法
    // getName(); 
    // 打印信息:
    // Animal()!
    // Cat()!
    // Animal()!
    // Dog()!
    // DogCat()!

    // 1.可以使用虚拟继承来解决此问题,2.当然也可以把方法声明成纯虚函数,即基类是抽象类不能被实例化
    // DogCat3.Animal::getName();
    Dog Dog1;
    Dog1.getName();
    DogCat3.getName();
    // 打印信息:
    //  Animal string getName()!
    //  Animal string getName()!
}

30.派生类没有实现相应的参数的构造方法但派生类可使用基类的构造方法,(using BaseClass::BaseClass,其实编译器默认实现了相应参数的构造方法,但是函数体是空的)

class Tree
{
public:
    Tree() = default;
    Tree(bool canWhat)
    {
        Util::LOGI("Tree(bool canWhat)!");
    }
};
class CherryTree : public Tree
{
public:
    using Tree::Tree;
    CherryTree()
    {
        Util::LOGI("CherryTree()!");
    }
};
void test20230620()
{
    CherryTree CherryTree1(true);
}

------------------------下面是C++Insight把原代码展开后的代码-----------------------------------
class Tree
{
  
  public: 
  inline constexpr Tree() noexcept = default;
  inline Tree(bool canWhat)
  {
  }
  
};


class CherryTree : public Tree
{
  
  public: 
  inline CherryTree()
  : Tree()
  {
  }
  
  inline CherryTree(bool canWhat) noexcept(false)
  : Tree(canWhat)
  {
  }
  
};


void test20230620()
{
  CherryTree CherryTree1 = CherryTree(true);
}

31.当派生类无论是override或者overload基类一个方法,此时用派生类调用其他参数版本的此方法,则一律走派生类override或者overload的这个方法,基类其他参数版本的此方法就被hide了,但可以通过基类指针或者引用正确调用到此方法,或者使用using声明使用基类的方法,或者override or overload其他参数版本的此方法

class Tree
{
public:
    Tree() = default;
    Tree(bool canWhat)
    {
        Util::LOGI("Tree(bool canWhat)!");
    }
    virtual void getHeight(bool)
    {
        Util::LOGI("Tree virtual void getHeight(bool)!");
    }
    void getHeight()
    {
        Util::LOGI("Tree void getHeight()!");
    }
};
class CherryTree : public Tree
{
public:
    using Tree::getHeight;
    CherryTree(bool)
    {
        Util::LOGI("CherryTree(bool)!");
    }
    void getHeight(bool) override
    {
        Util::LOGI("CherryTree void getHeight(bool) override!");
    }
};
void test20230620()
{
    CherryTree CherryTree1(true);

    CherryTree1.getHeight(false);
    // error: 当从基类override或者overload某个方法,此时基类的同名方法都会被hide,
    // 无论给什么参数都会走派生类的override或者overload的方法,除非使用using或者override或者overload所有其他参数版本的此方法
    // CherryTree1.getHeight();
    Tree &rTree = CherryTree1;
    rTree.getHeight(true);
    rTree.getHeight(); // 通过基类引用可以正确调用次方法
    // 打印信息:
    // CherryTree(bool)!
    // CherryTree void getHeight(bool) override!
    // CherryTree void getHeight(bool) override!
    // Tree void getHeight()!
}

32.当基类引用或者指针调用有默认参数的方法时,如果指向派生类,则调用的是派生类的方法,但是参数默认值是基类的,原因是C++使用表达式的编译时类型来绑定默认值参数,而不是运行时类型(C++20高级编程)

在这里插入图片描述
在这里插入图片描述

class Computer
{
public:
    int price;
    virtual void playGame(int hours = 2) const
    {
        Util::LOGI("Computer virtual void playGame(int hours = 2) const! param hours->%d", hours);
    }
};
class NoteBook : public Computer
{
public:
    // 基类virtual方法为public,private..,派生类为private,public..都视为override,
    // 但是如果基类为const,则派生类必须为const,否则视为overload
    void playGame(int hours = 4) const override
    {
        Util::LOGI("NoteBook void playGame(int hours = 4) const override! param hours->%d", hours);
    }
};
void test20230621()
{
    Computer Computer1;
    NoteBook NoteBook1;
    Computer &rComputer = NoteBook1;
    Computer *pComputer = &NoteBook1;
    Computer1.playGame();
    NoteBook1.playGame();
    rComputer.playGame();
    pComputer->playGame();
    // 打印信息:当基类引用或者指针调用有默认参数的方法时,如果指向派生类,则调用的是派生类的方法,但是参数默认值是基类的
    // Computer virtual void playGame(int hours = 2) const! param hours->2
    // NoteBook void playGame(int hours = 4) const override! param hours->4
    // NoteBook void playGame(int hours = 4) const override! param hours->2
    // NoteBook void playGame(int hours = 4) const override! param hours->2
}

33.虽然基类public virtual方法被派生类重写时,改成protected或者private,但是基类指针和引用当指向派生类时,也能调用派生类此重写方法.

class Computer
{
public:
    int price;
    virtual void playGame(int hours = 2) const
    {
        Util::LOGI("Computer virtual void playGame(int hours = 2) const! param hours->%d", hours);
    }
    virtual void listenMusic()
    {
        Util::LOGI("Computer virtual void listenMusic()!");
    }
    virtual void netChat()
    {
        Util::LOGI("Computer virtual void netChat()!");
    }
};
class NoteBook : public Computer
{
public:
    // 基类virtual方法为public,private..,派生类为private,public..都视为override,
    // 但是如果基类为const,则派生类必须为const,否则视为overload
    void playGame(int hours = 4) const override
    {
        Util::LOGI("NoteBook void playGame(int hours = 4) const override! param hours->%d", hours);
    }

protected:
    void listenMusic() override
    {
        Util::LOGI("NoteBook protected void listenMusic() override!");
    }

private:
    void netChat() override
    {
        Util::LOGI("NoteBook private void netChat() override!");
    }
};
void test20230621()
{
    // NoteBook1.listenMusic(); // error can not access protected method!!
    // NoteBook1.netChat(); // error can not access private method!!
    pComputer->listenMusic();
    pComputer->netChat();
    rComputer.listenMusic();
    rComputer.netChat();
    // 打印信息: 虽然基类public virtual方法被派生类重写时,改成protected或者private,但是基类指针和引用当指向派生类时,也能调用派生类此重写方法
    // NoteBook protected void listenMusic() override!
    // NoteBook private void netChat() override!
    // NoteBook protected void listenMusic() override!
    // NoteBook private void netChat() override!
}

34.在类方法成员中另一个实例化的对象也可以调用private protected成员,即拷贝构造等函数也可以正常拷贝private,protected成员,可以理解为相当于同类的方法互为friend方法.

class NoteBook
{
protected:
    void listenMusic() override
    {
        Util::LOGI("NoteBook protected void listenMusic() override!");
    }

private:
    void netChat() override
    {
        Util::LOGI("NoteBook private void netChat() override!");
    }

public:
    void callAnotherPrivateProtectedMethod(NoteBook &NoteBook1)
    {
        Util::LOGI("void callAnotherPrivateNetChat(NoteBook& NoteBook1)!");
        NoteBook1.netChat();
        NoteBook1.listenMusic();
    }
};
void test20230621()
{
    NoteBook NoteBook2;
    NoteBook NoteBook3;
    NoteBook3.callAnotherPrivateProtectedMethod(NoteBook2);
    // 打印信息:在类方法成员中另一个实例化的对象也可以调用private protected成员
    // void callAnotherPrivateNetChat(NoteBook& NoteBook1)!
    // NoteBook private void netChat() override!
    // NoteBook protected void listenMusic() override!
}

35.typeid用来判断对象类型,如果是多态类型(基类至少有一个virtual方法),typeid返回的则是动态类型类型信息.

class Phone
{
public:
    virtual void call()
    {
        Util::LOGI("Phone virtual void call()");
    }
};
class XiaoMi : public Phone
{
};

void test20230625()
{
    XiaoMi XiaoMi1;
    Phone &Phone1 = XiaoMi1;
    Util::LOGI("typeid(Phone1)->%s,typeid(XiaoMi)->%s", typeid(Phone1).name(), typeid(XiaoMi).name());
    if (typeid(Phone1) == typeid(XiaoMi))
    {
        Util::LOGI("typeid(Phone1) == typeid(XiaoMi) is true!");
    }
    // typeid用来判断对象类型,如果是多态类型,基类至少有一个virtual方法,typeid返回的则是动态类型类型信息.
    // 需要的头文件是<typeinfo>
    // 打印信息:
    // typeid(Phone1)->6XiaoMi,typeid(XiaoMi)->6XiaoMi
    // typeid(Phone1) == typeid(XiaoMi) is true!
}

36.C++常用的4种转换,const_cast,static_cast,dynamic_cast,reinterpret_cast.(C++20高级编程)

在这里插入图片描述

class Car
{
};
class AirPlane
{
};
class SUVCar : public Car
{
public:
    using PMethod = void (*)();
    SUVCar() = default;
    SUVCar(SUVCar &&)
    {
        Util::LOGI("SUVCar(SUVCar &&)!");
    }
    SUVCar(const SUVCar &)
    {
        Util::LOGI("SUVCar(const SUVCar&)!");
    }

    static void method()
    {
        Util::LOGI("void method()!");
    }
    operator PMethod()
    {
        Util::LOGI("operator PMethod()!");
        return method;
    }
    SUVCar &operator=(const SUVCar &) = default;
};
void test20230626()
{
    SUVCar SUVCar1;
    SUVCar SUVCar2 = SUVCar1;            // copy constructor
    SUVCar SUVCar3 = std::move(SUVCar1); // move constructor
    // 信息打印:
    // SUVCar(const SUVCar&)!
    // SUVCar(SUVCar &&)!

    const string &rStr = "12344444444";
    string &rStr1 = const_cast<string &>(rStr);
    Util::LOGI("before const string &rStr-->%s,string &rStr1-->%s", rStr.c_str(), rStr1.c_str());
    rStr1 = "3333333333333333";
    Util::LOGI("after const string &rStr-->%s,string &rStr1-->%s", rStr.c_str(), rStr1.c_str());
    // 打印信息://const_cast;1.只能用于指针或者引用 2.不能转换为其他类型,只是去掉const限定符
    // before const string &rStr-->12344444444,string &rStr1-->12344444444
    // after const string &rStr-->3333333333333333,string &rStr1-->3333333333333333

    SUVCar::PMethod PMethod1 = SUVCar3; // 调用类型转换函数
    PMethod1();
    // 打印信息:
    // operator PMethod()!
    // void method()!

    int intNumber = 0;
    // static_cast使用基本等价于隐式转换
    // 用于低风险的转换,一般只要编译器能自己进行隐式转换的都是低风险转换,一般平等转换和提升转换都是低风险的转换
    // 等价于double doubleNumber =static_cast<double>(intNumber)(c++insight中也是这样展开的);
    double doubleNumber = intNumber;
    // 可用于派生类指针转换成基类指针,基类指针转化成子类指针
    // 下面均编译ok
    SUVCar *pSUVCar = nullptr;
    Car *pCar = nullptr;
    pSUVCar = static_cast<SUVCar *>(pCar);
    SUVCar *pSUVCar1 = nullptr;
    pCar = static_cast<Car *>(pSUVCar1);
    SUVCar SUVCar4;
    Car Car1;
    SUVCar &rSUVCar = SUVCar4;
    Car &rCar = Car1;
    rSUVCar = static_cast<SUVCar &>(rCar);
    SUVCar &rSUVCar1 = SUVCar4;
    rCar = static_cast<Car &>(rSUVCar1);
    Car1 = static_cast<Car>(SUVCar4); // ok
    // SUVCar4 = static_cast<SUVCar>(Car1); // error
    // Car1 = reinterpret_cast<Car>(SUVCar4); // error

    // 强制转换,不同类型,引用的指针转换,指针和long long之类的转换,不能不同对象之前的转换
    Car *Car2 = nullptr;
    AirPlane *AirPlane1 = nullptr;
    Car2 = reinterpret_cast<Car *>(AirPlane1);
    Car *Car3 = nullptr;
    AirPlane1 = reinterpret_cast<AirPlane *>(Car3);
    Util::LOGI("long size->%d,int size->%d,long int size->%d,long long size->%d", sizeof(long), sizeof(int), sizeof(long int), sizeof(long long));
    // 打印信息:
    // long size->4,int size->4,long int size->4,long long size->8
    long long intCar = reinterpret_cast<long long>(Car3);
}

37.解决循环依赖可以使用前置声明,这样还可以减少重新编译次数,因为前置声明可以不用包含相关头文件,当此头文件改变时,前置声明的类型,不会重新编译.(C++20高级编程)

在这里插入图片描述

38.全局方法当只有声明没有定义的时候是外部链接,全局static或者匿名命名空间的全局方法是内部链接(只能在相同的源文件中访问).

External.cpp
#include "Util.hpp"
void externalMethod();
static void staticInternalMethod();
void externalMethod()
{
    mingzz::Util::LOGI("External.cpp void externalMethod()!");
}

void staticExternalMethod()
{
    mingzz::Util::LOGI("External.cpp void staticExternalMethod()!");
}

namespace
{
    void anonymousNameSpaceMethod();
    void anonymousNameSpaceMethod()
    {
        mingzz::Util::LOGI("External.cpp void anonymousNameSpaceMethod()!");
    }
}
void externalMethod(); // external link
// multiple definition of `externalMethod()'
//  void externalMethod(){
//      Util::LOGI("CplusplusStudy.cpp void externalMethod()!");
//  }
void staticExternalMethod(); // static method is internal link only same source file can access it
void anonymousNameSpaceMethod();

void test20230627()
{
    externalMethod();
    // 打印信息:// 全局方法若只有声明没有定义,则是外部链接,即在其他文件中找实现
    //  External.cpp void externalMethod()!

    // external static method is internal link only same source file can access it
    // staticExternalMethod(); //error

    // error anonymous NameSpace下的external method is also internal link the same as external static method
    // anonymousNameSpaceMethod(); //error internal link can not access it in different file
}

39.extern一个用法,extern int x,它只代表一个声明没有定义,在一个文件声明并赋值(可以不用声明成extern),然后在另一个文件的使用处,必须使用extern声明才能使用前一个文件声明定义的变量.(C++20高级编程)

在这里插入图片描述

40.方法内部的静态变量,在方法结束后不会被销毁,且只能在此方法中使用,可用来记录一些信息.(C++20高级编程)

在这里插入图片描述

class Haval
{
public:
    void traval()
    {
        static int day = 1;
        Util::LOGI("My Haval go to traval %d days!", day);
        ++day;
    }
};

void test20230628()
{
    Haval Haval1;
    for (int i = 0; i < 5; i++)
    {
        Haval1.traval();
    }
    // 打印信息://方法内部的静态变量,在方法结束后不会被销毁,且只能在此方法中使用
    //  My Haval go to traval 1 days!
    //  My Haval go to traval 2 days!
    //  My Haval go to traval 3 days!
    //  My Haval go to traval 4 days!
    //  My Haval go to traval 5 days!
}

41.不同文件的Nonlocal变量初始化顺序是未定义的(C++20高级编程).

42. 类的const成员函数和非const成员函数(函数名,参数列表完全一样),可以根据调用对象的常量性来决定调用哪个版本的函数(类似于函数重载只不过不是根据参数列表不同来区分,而是根据调用对象来区分)

class Run
{
public:
    int getNumber()
    {
        Util::LOGI("Run : int getNumber()!");
        return 9;
    }
    int getNumber() const
    {
        Util::LOGI("Run : int getNumber() const !");
        return 5;
    }
    // 无法重载仅按返回类型区分的函数:
    //  const int getNumber(){
    //      return 4;
    //  }
    int getNumber(int) const
    {
        Util::LOGI("Run : int getNumber(int) const !");
        return 0;
    }
};
void test20231002()
{
    Run run;
    run.getNumber();
    // 打印信息:
    // Run : int getNumber()!
    const Run run1;
    run1.getNumber();
    // 打印信息:
    // Run : int getNumber() const !
    run.getNumber(0); // 但是非常量对象是可以调用常量函数的
    // 打印信息:
    // Run : int getNumber(int) const !
}

43. std::unique_ptr<> 和 std::make_unique<>()关于临时unique_ptr的析构以及move函数的正确解释

    class Run
    {
    public:
        int getNumber()
        {
            Util::LOGI("Run : int getNumber()!");
            return 9;
        }
        int getNumber() const
        {
            Util::LOGI("Run : int getNumber() const !");
            return 5;
        }
        // 无法重载仅按返回类型区分的函数:
        //  const int getNumber(){
        //      return 4;
        //  }
        int getNumber(int) const
        {
            Util::LOGI("Run : int getNumber(int) const !");
            return 0;
        }
        Run(const Run &)
        {
            Util::LOGI("Run : Run(const Run&)!");
        }
        Run(Run &&)
        {
            Util::LOGI("Run : Run( Run&&)!");
        }
        Run()
        {
            Util::LOGI("Run : Run()!");
        }
        ~Run()
        {
            Util::LOGI("Run : ~Run()!");
        }
    };
    Run run4;
    /**
     *  step1:
     *  会调用unique_ptr的移动构造函数,因为std::make_unique()会
     *  创建个临时的unique_ptr对象,然后会直接赋值给需要赋值的对象,并
     *  不会调用拷贝构造函数。(c++应该对函数返回值,进行过优化!)
     *
     *  step2:
     *  会调用unique_ptr的移动赋值操作函数,因为std::make_unique()会
     *  创建个临时的unique_ptr对象,虽然unique_ptr的移动赋值操作函数是
     *  default,但是他的成员的移动赋值操作函数会把这个临时的unique_ptr对象
     *  的指向Run*的值赋值给uPtr2,然后把自己的指向Run*的值赋值为nullptr
     *  然后把uPtr2原来指向的Run*内容给释放掉,然后这个临时的unique_ptr在被
     *  析构掉。
     */
    auto uPtr2 = std::make_unique<Run>(run4); // step1
    uPtr2 = std::make_unique<Run>(run4);      // step2
    // 打印信息:
     /**
     * Run : Run()!
     * Run : Run(const Run&)!
     * Run : Run(const Run&)!
     * Run : ~Run()!
     */

44. 模板template编程中,编译器会选择性实例化,虚拟方法和代码中用的方法,编译器才会自动生成相关代码定义,其他没使用到的方法代码则只会生成个声明。(C++20高级编程)

在这里插入图片描述
以下是c++ insight 的代码示例:

这是编译前的代码:
在这里插入图片描述

下面是进一步编译代码后的结果:
在这里插入图片描述
在这里插入图片描述

45. 模板参数,可以像方法参数一样有默认值,还可以有默认类型,创建对象的时候可以省略一些有默认的参数。(C++20高级编程)

在这里插入图片描述
c++ insight 代码示例:

编译前的代码:
在这里插入图片描述
编译后的代码:在这里插入图片描述

46. 模板编程中可以自己创建模板参数推导规则。(C++20高级编程)

在这里插入图片描述
c++ insight 示例代码:

编译前的代码:
在这里插入图片描述

编译后的代码:
在这里插入图片描述

47. C++中想以数组为参数,可以用数组指针或者数组引用,直接用数组作为参数,编译器会默认数组参数类型是指针

c++ 实例代码:

c++ insights 编译前的代码:
在这里插入图片描述

c++ insights编译后的代码:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值