【c++基础】第三章 宏观部分:面向对象之类的层次结构与相互关系

news2024/11/15 13:46:21

第三章 宏观部分:面向对象之类的层次结构与相互关系

  • 关系
    • has_a关系
    • use_a关系(友元关系)
    • is_a关系(继承关系)
      • 单继承
      • 多继承
      • 菱形继承
  • ==多态(polymorphic)==
    • 虚函数
    • 多态底层机制:虚函数、虚函数表指针、虚函数表
    • 多态实例:
    • 多态问题
    • 纯虚函数与抽象类
  • 类型转换
    • 静态类型转换运算符:static_cast
    • 动态类型转换运算符:dynamic_cast
    • 常类型转换运算符:const_cast
    • 解释类型转换运算符:reinterpret_cast
  • 临时对象与C++11右值引用
    • 临时对象
    • 右值引用
  • 移动构造函数
  • c++异常处理机制

关系

  • has_a关系
  • use_a关系
  • is_a关系

has_a关系

又名:包含,组合,层次,复合关系

是一个类,由其他类对象共同组成

#include <iostream>

using namespace std;

class Desk{
    int a;
public:
    Desk(){
        cout << "Desk构造" << this << endl;
    }
    ~Desk(){
        cout << "Desk析构" << endl;
    }
};

class Person{
    int a;
public:
    Person(){
        cout << "Person构造" << this << endl;
    }
    ~Person(){
        cout << "Person析构" << endl;
    }
    void say(){
        cout << "hello" << endl;
    }
};

class Chair{
    int a;
public:
    Chair(){
        cout << "Chair构造" << this << endl;
    }
    ~Chair(){
        cout << "Chair析构" << endl;
    }
};

class Room{
private:
    Person p;
    Desk d;
    Chair c;
public:
    Room(){
        cout << "Room构造" << this << endl;
    }
    ~Room(){
        cout << "Room析构" << endl;
    }
};

int main()
{
    Room r;
    Room *r1=new Room();
    ((Person *)(r1))->say();
    delete r1;
    return 0;
}

has_a关系中构造与析构的先后顺序:

  • 构造:先顺序构造类中成员对象,最后构造最外层对象
  • 析构:先析构最外层对象,然后逆序析构类中成员对象

内存关系布局图:
在这里插入图片描述

use_a关系(友元关系)

类与函数,类与类之间的亲密关系。

存在两种形式:友元函数友元类

友元函数:

语法:friend 函数声明;

在类中使用friend 关键字声明类外的某个函数为本类的友元函数,类内即生效

特性:

  • 当友元函数有本类的对象时,在友元函数中对象访问类中的属性是不受权限约束的
#include <iostream>

using namespace std;

class Boy{
    int money;
public:
    Boy(int money){
        this->money=money;
    }

    friend void beautifulGril(Boy &boy);//friend关键字,声明成为友元函数
    
};

void beautifulGril(Boy &boy){
    cout << boy.money << endl;
}

int main()
{
    Boy boy(10000);

    beautifulGril(boy);
    
    return 0;
}

友元类:

语法:friend class 友元类名

单文件,只能实现单友谊

#include <iostream>

using namespace std;

class Boy{
private:
    string name;
    int age;
public:
    Boy(string name,int age){
        this->name=name;
        this->age=age;
    }
    void hard_work(){
        cout << "woking" << endl;
    }
    
    friend class Gril;
};

class Gril{
private:
    string name;
    int age;
public:
    Gril(string name,int age){
        this->name=name;
        this->age=age;
    }
    void go_shopping(){
        cout << "shopping" << endl;
    }
    void go_shopping(Boy& boy){
        cout << boy.name << "  .ing  " << this->name << "  shopping  " << endl;
    }
    
};

int main()
{
    Boy boy("yao",18);
    Gril gril("sun",17);
    gril.go_shopping(boy);
    return 0;
}

分文件编程

boy.h:

#ifndef BOY_H
#define BOY_H
#include <iostream>
using namespace std;
#include "girl.h"
class Girl;

class Boy
{
public:
    string name;
    int age;
public:
    Boy(string name,int age);
    void hard_work(Girl& girl);
    friend class Girl;
};

#endif // BOY_H

girl.h:

#ifndef GRIL_H
#define GRIL_H
#include <iostream>
using namespace std;
#include "boy.h"
class Boy;

class Girl
{
public:
    string name;
    int age;
public:
    Girl(string name,int age);
    void goshopping(Boy& boy);
    friend class Boy;
};

#endif // GRIL_H

boy.c:

#include "boy.h"
#include "girl.h"

Boy::Boy(string name,int age)
{
    this->name=name;
    this->age=age;
}

void Boy::hard_work(Girl& girl){
   cout << girl.name << "  accompany.ing  " << this->name << "  Set out for Happiness" << endl;
}

girl.c:

#include "girl.h"

Girl::Girl(string name,int age)
{
    this->name=name;
    this->age=age;
}
void Girl::goshopping(Boy &boy){
    cout << boy.name << "  be being  " << this->name << "  shopping" << endl;
}

main.c:

#include <iostream>
#include "girl.h"
#include "boy.h"

using namespace std;

int main()
{
    Boy boy("liangliang",18);
    Girl girl("minmin",17);
    girl.goshopping(boy);
    boy.hard_work(girl);
    return 0;
}

不存在传导关系(即A与B是好友,B与C是好友,A和C不一定是好友)

is_a关系(继承关系)

继承(派生)作用:提高代码复用性和高拓展性

单继承

语法:class+子类 :继承权限+父类

在这里插入图片描述

#include <iostream>

using namespace std;

class Car{
    int weight;
public:
    void run(){
        cout << "runing" << endl;
    }
};

class BMW : public Car//is a Car  类
{
public:
    
    int size;
    void show(){
        cout << "i am is BMW" << endl;
    }
};

class BNZ : private Car//is a Car  类
{
public:
    int size;
    void show(){
        this->run();
        cout << "i am is BNZ" << endl;
    }
};

class TRAM : public BMW//is a BMW  类
{
public:
    void say(){
        cout << "my father is BMW" << endl;
    }
};

int main()
{
    cout << sizeof (Car) << endl;
    Car c;
    c.run();

    cout << sizeof (BMW) << endl;
    BMW b;
    b.run();
    b.show();

    TRAM tram;
    cout << sizeof (TRAM) << endl;
    tram.run();
    tram.show();
    tram.say();

    BNZ bnz;
    bnz.show();
    return 0;
}

理解:抽象定义基类(父类),继承(派生)子类(继承类)基类本身功能不可修改,此原则叫做开闭原则,即拓展开放,对修改关闭。

继承关系中构造与析构的先后顺序:

  • 构造:先顺序构造父类中成员对象,最后构造子类中对象
  • 析构:先析构子类对象,然后逆序析构父类中成员对象当子类析构触发,父类析构也执行。

单继承关系下内存布局:
在这里插入图片描述

#include <iostream>

using namespace std;

class A{
    int a;
public:
    A(){
       cout << "i am is A structure" << this <<  endl;
    }
    ~A(){
       cout << "i am is A destruct" << endl;
    }
};

class B : public A{
    int b;
public:
    B(){
       cout << "i am is B structure" << this << endl;
    }
    ~B(){
       cout << "i am is B destruct" << endl;
    }
};

int main()
{
    A a;
    B b;
    return 0;
}

注意:派生中的基类,不同于本身类

解决同名问题(隐藏机制):

当继承类与基类存在同名,基类隐藏在类域之中,如果想访问基类,方法:域名::访问

#include <iostream>

using namespace std;

class A{
public:
    int a=100;
    A(){
       cout << "i am is A structure" << this <<  endl;
    }
    ~A(){
       cout << "i am is A destruct" << endl;
    }
    void show(){
        cout << "A inside" << endl;
    }
};

class B : public A{
public:
    int a=1000;
    B(){
       cout << "i am is B structure" << this << endl;
    }
    ~B(){
       cout << "i am is B destruct" << endl;
    }
    void show(){
        cout << "B inside" << endl;
    }
};

int main()
{
    B b;
    cout << b.a << endl;//1000
    cout << b.A::a << endl;//100
    b.show();//B inside
    b.A::show();//A inside
    return 0;
}

类型兼容规则is_a关系:子类是基类的一个拓展。

兼容语法如下:

  1. 基类指针可以安全的访问继承类对象
  2. 当父类不存在默认构造时,应在继承类的构造函数的初始化列表中初始化
  3. 父类指针只能调用子类类域中的所包含的基类中的类域方法和属性,但存在继承类不能析构问题。
  4. 所以基类指针释放资源时,要强转成子类指针才能释放,避免内存泄漏。
#include <iostream>

using namespace std;
class Person{
private:
    string name;
    int age;
public:
    Person(string name,int age){
        this->name=name;
        this->age=age;
        cout << "Person structure" << endl;
    }
    void showInfo(){
        cout << "姓名:" << this->name << ",年龄" << this->age << endl;
    }
    int getAge(){
        return  this->age;
    }
    string getName(){
        return  this->name;
    }
    ~Person(){
        cout << "Pseron desturct" << endl;
    }
};

class Stu:public Person
{
private:
    int score;
    int *p;
public:
    //当父类中没有默认构造,应该在子类的构造函数的初始化列中指定。
    Stu(string name,int age,int score):Person(name,age)
    {
        this->score=score;
        this->p=new int[1024];
        cout << "stu structure" << endl;
    }
    void showInfo(){
        cout << "姓名:" << this->getName() << ",年龄" << this->getAge() << ",成绩" << score <<endl;
    }
    ~Stu(){
        delete []p;
        cout << "stu desturct" << endl;
    }
};

int main()
{
    Stu s("yao",19,100);
    s.showInfo();
    Person* p=new Stu ("sun",10,100);
    p->showInfo();
//    Stu* stu=new  Person("yao",19);//不能子类指向父类
    //查看基类指针是否指向继承类(继承类是否开辟空间),方法:把基类指针强转成继承类指针
    ((Stu*)p)->showInfo();

    //强转存在风险,原因:继承类可能不开辟空间
    cout << "强制转换的风险性" << endl;
    Person* q=new Person("minmin",18);
    q->showInfo();
    ((Stu*)q)->showInfo();
    delete (Stu*)p;
    delete q;
    return 0;
}

多继承

语法:class+子类:继承权限1+父类1,继承权限2+父类2,继承权限n+父类n

  • 优点:可以大大提高代码的复用性

  • 缺点:

    • 存在代码冗余,占用空间大,资源浪费
    • 存在二义性问题,当多个父类中,有同名的函数或属性时,还需要使用类域::域名解析符访问,带来不便
  • 推荐:使用多继承多个抽象类,因为抽象类没有具体的属性,只有**纯虚函数**

多继承的内存布局:

在这里插入图片描述
父类指针指向子类对象时:

  1. 父类指针永远指向父类类域的起始地址。
  2. 如果要释放内存,必须要强转成继承类类型,避免内存泄露。
#include <iostream>

using namespace std;

class Phone{
public:
    string logo;
    int power;
public:
    Phone(){
        cout << "Phone structure" << this << endl;
    }
    ~Phone(){
        cout << "Phone destruct" << endl;
    }
    void display(){
        cout << "手机正在显示中" << endl;
    }
};

class Computer{
public:
    string logo;
    int power;
public:
    Computer(){
        cout << "Computer structrue" << this << endl;
    }
    ~Computer(){
        cout << "Computer deatruct" << endl;
    }
    void display(){
        cout << "电脑正在显示中" << endl;
    }
};

class Pad:public Phone,public Computer
{
public:
    Pad(){
        cout <<"Pad structrue"<< this << endl;
    }
    ~Pad(){
        cout <<"Pad destruct"<< endl;
    }
    void playgame(){
        cout << this->Phone::logo << endl;
        this->Phone::display();
        cout << this->Phone::power << endl;
    }
};

int main()
{
    cout << sizeof(Pad) << endl;
    Pad p;
    p.playgame();
    Phone* phone=new Pad ();
    cout << phone << endl;
    delete (Pad*)phone;

    Computer* computer=new Pad ();
    cout << computer << endl;
    delete (Pad*)computer;
    return 0;
}

菱形继承

分析图:
在这里插入图片描述
常看到的模型:
在这里插入图片描述
问题解决(virtual关键字):虚继承

#include <iostream>

using namespace std;

class A{
public:
    int a;
    string name;
    A(){
       cout << "i am is A structure" << this <<  endl;
    }
    ~A(){
       cout << "i am is A destruct" << endl;
    }
};

class B :virtual  public A{
public:
    int b;
    B(){
       cout << "i am is B structure" << this << endl;
    }
    ~B(){
       cout << "i am is B destruct" << endl;
    }
};

class C :virtual public A{
public:
    int c;
    C(){
       cout << "i am is C structure" << this << endl;
    }
    ~C(){
       cout << "i am is C destruct" << endl;
    }
};

class D : public B,public C{
public:
    int d;
    D(){
       cout << "i am is D structure" << this << endl;
    }
    ~D(){
       cout << "i am is D destruct" << endl;
    }
};

int main()
{
    D d;
    cout << sizeof (d) << endl;//没有加virtual 96 个
    //加后72个,极大的节省了空间
    //原因,virtual会安插虚基表指针,生成两个指针
    //A为40个字节,B中有一个指针一个int,对齐后16个字节,C中有一个指针一个int,对齐后16个字节,D中int进行对齐,所以共72字节
    return 0;
}

虚基表指针指向虚基表
虚基表保存着虚基表偏移量
通过偏移量找到父类中的属性

开发经验:

has_a>单继承>多继承(多个抽象类)>菱形继承(virtual)

多态(polymorphic)

动态多态、绑定多态

形成条件(必须满足三条):

  1. 继承关系
  2. 父类中存在虚函数,子类中完成对父类虚函数的重写
  3. 使用父类指针或引用,指向或引用子类实例

虚函数

类中成员函数前加virtual,特殊的构造函数不能加vritual

虚函数特性:类中的虚函数具有虚属性,也就是说函数逻辑可以在子类之中完成重写

重写:即是类似于子类之中对父类中的同名函数的重新定义

两个关键字:override(检查重写)与final(结束重写)

#include <iostream>

using namespace std;

class Person{
private:
    string name;
    int age;
public:
    Person(string name,int age){
        this->name=name;
        this->age=age;
    }
    virtual void showInfo(){
        cout << "姓名:" << this->name << ",年龄:" << this->age << endl;
    }
    int getAge(){
        return  this->age;
    }
    string getName(){
        return  this->name;
    }
};

class Stu:public Person{
private:
    int score;
public:
    Stu(string name,int age,int score):Person(name,age){
        this->score=score;
    }
    //c++11提供的信关键字override重写,用来检查重写父类虚函数的返回值与形参类型是否匹配,不匹配error
    //关键字final进行重写结束(替换在override位置上)
    void showInfo()override{//同名就是虚函数
        cout << "姓名:" << this->getName() <<"年龄:"  << this->getAge() << "成绩:" << score << endl ;
    }
};

int main()
{
    Person* person=new Stu("yao",19,100);
    person->showInfo();//现在是子类的,因为加virtual,进行多态重写
    Person* p=new Stu ("minmin",18,90);
    p->showInfo();
    ((Stu*)person)->showInfo();
    Stu stu("sunsun",18,200);
    stu.Person::showInfo();
    stu.showInfo();
    return 0;
}

多态底层机制:虚函数、虚函数表指针、虚函数表

关系解析图:

在这里插入图片描述
虚函数表指针由构造函数创建 .rodata段存放虚函数表 虚函数表存放虚函数的地址偏移量

**通过virtual关键字进行重写 **override关键字进行检查重写 final关键字进行结束重写

多态实例:

实现飞行员可以驾驶各类飞机:

思路:

  1. 创建一个基类(飞行员),定义一个虚函数,用于多态继承
  2. 创建多个继承类,共同继承基类,功能实现
#include <iostream>

using namespace std;
class Plan{
public:
    virtual void fly(){
        cout << "飞机正在飞" << endl;
    }
};

class AirBus:public Plan{
public:
    void fly()override{
        cout << "客中客车在飞行" << endl;
    }
};

class J20:public Plan{
public:
    void fly()override{
        cout << "J20在飞行" << endl;
    }
};

class Polit{
public:
    void driver(Plan& plan){//Plan& plan=
        plan.fly();
    }
};

int main()
{
    AirBus airbus;
    J20 j20;
    Polit polit;
    polit.driver(airbus);
    polit.driver(j20);
    return 0;
}

多态问题

问题子析构无法得到执行

解决方案虚析构(基类析构函数为虚析构)

好处避免内存泄漏,无需强转

原理多态状态下,子析构进行重写

#include <iostream>

using namespace std;

class Person{
private:
    string name;
    int age;
public:
    Person(string name,int age){
        this->name=name;
        this->age=age;
        cout << "Person structure" << endl;
    }
    virtual void showInfo(){
        cout << "姓名:" << this->name << ",年龄:" << this->age << endl;
    }
    int getAge(){
        return  this->age;
    }
    string getName(){
        return  this->name;
    }
    virtual ~Person(){ //设计为虚析构
        cout << "Person destruct" << endl;
    }
};

class Stu:public Person{
private:
    int score;
public:
    Stu(string name,int age,int score):Person(name,age){
        this->score=score;
        cout << "Stu structure" << endl;
    }
    //c++11提供的信关键字override重写,用来检查重写父类虚函数的返回值与形参类型是否匹配,不匹配error
    //关键字final进行重写结束(替换在override位置上)
    void showInfo()override{//同名就是虚函数
        cout << "姓名:" << this->getName() <<"年龄:"  << this->getAge() << "成绩:" << score << endl ;
    }
    ~Stu()override{
        cout << "Stu destruct" << endl;
    }
};

int main()
{
    Person* person=new Stu("yao",19,100);
    person->showInfo();//现在是子类的,因为加virtual,进行多态重写
    delete (Stu*)person;
    cout << "-------------------------------------------------" << endl;
    Person* p=new Stu ("minmin",18,90);
    p->showInfo();
    delete p;  //不强转,子析构不会存在,解决方案:虚析构
    cout << "-------------------------------------------------" << endl;
    Person* q=new Person("sun",18);
    q->showInfo();
    delete (Stu*)q;//存在问题,如果不是多态,会报错
    cout << "-------------------------------------------------" << endl;

    ((Stu*)person)->showInfo();
    Stu stu("sunsun",18,200);
    stu.Person::showInfo();
    stu.showInfo();
    return 0;
}

纯虚函数与抽象类

介绍:无函数体,无任何逻辑,是特殊的虚函数,是作为函数API使用的

逻辑:在子类中重写

分析:用名字实现功能,本身不定义,由子类继承后进行重写定义,别名《接口函数》

应用:实现多态,顶层类的设计使用

规则:该抽象类是不可以定义对象的,只能使用指针或引用,指向或引用子类重写纯虚函数的子类对象。

包含纯虚函数的类,也叫做抽象类,也被称接口类

如果子类没有重写纯虚函数,那么子类也将成为抽象类

#include <iostream>

using namespace std;

class A{
public:
    virtual void showInfo()=0;
    A(){
        cout << "A structure" << endl;
    }
    ~A(){
        cout << "A destruct" << endl;
    }
};

class B:public A{
public:
    void showInfo() override{
        cout << "hello world" << endl;
    }
    B(){
        cout << "B structure" << endl;
    }
    ~B(){
        cout << "B destruct" << endl;
    }
};

class C:public A{
public:
    void showInfo() override{
        cout << "hi world" << endl;
    }
    C(){
        cout << "C structure" << endl;
    }
    ~C(){
        cout << "C destruct" << endl;
    }
};

class D{
public:
    virtual void showInfo_D()=0;
    D(){
        cout << "D structure" << endl;
    }
    ~D(){
        cout << "D destruct" << endl;
    }
};

class E:public A,public D{
public:
    void showInfo() override{
        cout << "hello world" << endl;
    }
    void showInfo_D() override{
        cout << "hi world" << endl;
    }
    E(){
        cout << "E structure" << endl;
    }
    ~E(){
        cout << "E destruct" << endl;
    }
};

int main()
{
    A *a=new B ();
    a->showInfo();
    delete (B*)a;
//    A b;//error,不能定义接口类,只能定义指针或引用,指向或引用子类
//    A* b=new A ();//error,不能定义接口类,只能定义指针或引用,指向或引用子类
    A* c=new C();
    c->showInfo();
    delete (C*)c;
    cout << "-------------------解决多继承问题-------------------" << endl;
    A* e=new E ();
    e->showInfo();
    delete (E*)e;
    D* f=new E ();
    f->showInfo_D();
    delete (E*)f;
    E h;
    h.showInfo();
    h.showInfo_D();
    return 0;
}

类型转换

强转,临时有效

静态类型转换运算符:static_cast

可转基本类型

#include <iostream>

using namespace std;

int main()
{
    int a=500;
    double b=3.14;
    cout << (int)b << endl;
    cout << static_cast<int>(b) << endl;

    double* p=&b;
    cout << *p << endl;
//    cout << *(int *)p << endl;//只取小端后4个字节
//    cout << *(static_cast<int *>(p)) << endl;//static_cast强转基本类型指针不被允许,这是一种安全保护
    return 0;
}

不同基本类型之间避免转换

可转自定义类型

#include <iostream>

using namespace std;

class A{
private:
    string my_Astr="la la la la" ;
public:
    void showInfo_A(){
        cout << this->my_Astr << endl;
    }
};

class B: public A{
private:
    string my_Bstr="ha ha ha ha" ;
public:
    void showInfo_B(){
        cout << this->my_Bstr << endl;
    }
};

int main()
{
    A* aa=new B ();
    ((B*)aa)->showInfo_B();
    static_cast<B*>(aa)->showInfo_B();

    A* a=new A();
    //存在的问题
//    ((B*)a)->showInfo_B();
//    static_cast<B*>(a)->showInfo_B();// 出现bug
    return 0;
}

在继承类中,向下转型并不安全,向上是安全的,原因:可能不会开辟空间,安全由程序员来保证

动态类型转换运算符:dynamic_cast

依赖多态完成,会去查看虚函数表中的运行时类型信息识别槽

如果不存在,返回nullptr,直接杀死进程,并报错安全

#include <iostream>

using namespace std;

class A{
private:
    string my_Astr="la la la la" ;
public:
    void showInfo_A(){
        cout << this->my_Astr << endl;
    }
    virtual ~A()=default; //使用c++11的默认函数体
};

class B: public A{
private:
    string my_Bstr="ha ha ha ha" ;
public:
    void showInfo_B(){
        cout << this->my_Bstr << endl;
    }
};

int main()
{
    A* aa=new B ();
    ((B*)aa)->showInfo_B();
    static_cast<B*>(aa)->showInfo_B();
    dynamic_cast<B*>(aa)->showInfo_B();

    A* a=new A();	
//    ((B*)a)->showInfo_B();
//    static_cast<B*>(a)->showInfo_B();// 出现bug
//    dynamic_cast<B*>(a)->showInfo_B();//直接杀死进程
    return 0;
}

常类型转换运算符:const_cast

使用场景改变指针或者引用的常属性

#include <iostream>

using namespace std;

int main()
{
    int a=100;
    const int *p=&a;
    *((int*)p)=500;
    cout << a << endl;
    *const_cast<int*>(p)=500;
    cout << a << endl;
    cout << *p << endl;

    cout << "-----------------改变常引用类型----------" << endl;
    const int& b=a;
    const_cast<int &>(b)=600;
    cout << b << endl;
    cout << a << endl;
    
    int c=10;
    int* const q=&a;
    //const_cast<int *>(q)=&c;//const不能改变指针的指向,原因:const_cast的设计是根据c++引用而来的,引用不能只能初始化一次
    return 0;
}

注意:

const不能改变指针的指向,原因:const_cast的设计是根据c++引用而来的,引用只能初始化一次

解释类型转换运算符:reinterpret_cast

应用场景:任意类型间的转换

范围:广泛

难度:困难

分析:特别底层,没有类型检查和格式转换,仅仅是简单的二进制转换不安全

#include <iostream>

using namespace std;

int main()
{
    int a=100;
    int *p=&a;
    cout << reinterpret_cast<long long>(p) << endl;
    return 0;
}

临时对象与C++11右值引用

临时对象

  • 生命周期临时对象声明短暂,仅存在一行
  • 作用用来进行数据传递,会发生拷贝,放在栈中

右值引用

作用用引用引用右值引用

底层实现引用右值在栈上的临时变量,是临时变量的别名

规则只能进行右值引用,不能引用左值(类型不匹配,相当于指针与类型)

解决移动语句函数move

#include <iostream>

using namespace std;

class A{
public:
    virtual void showInfo(){
        cout << "i am is A" << endl;
    }
    A(const A& other){
        cout << "A copy" << endl;
    }
    A(){
        cout<< "A structure" << endl;
    }
    virtual ~A(){
        cout << "A destruct" << endl;
    }
};

class B:public A{
public:
    void showInfo()  override{
        cout << "i am is B" << endl;
    }
    B(const B& other){
        cout << "B copy" << endl;
    }
    B(){
        cout<< "B structure" << endl;
    }
    virtual ~B(){
        cout << "B destruct" << endl;
    }
};

int main()
{
    A* a=new B ();
    a->showInfo();
    delete (B*)a;
//    A a1=B();//B是临时变量,单纯一个临时对象,用来进行数据传递,赋值给a1时会发生拷贝,放在栈中
//    static_cast<B*>(a1).showInfo();//error,原因临时对象声明短暂,仅存在一行
//    a1.showInfo();

    //c++中的语法糖
    cout << "---------------------语法糖----------------" << endl;
    const A& a2=B();//B temp=B();(此处发生一次编译优化:RVO编译优化)  const A& a2=temp;
//    a2.showInfo();//在类函数中加const或者强转
    const_cast<A&>(a2).showInfo();//都很麻烦,所以右值引用出现

    //c++11的右值引用,右值是没有办法取地址的,如:常量,临时对象
    cout << "---------------------右值引用----------------" << endl;
    A&& a3=B();
    a3.showInfo();

    //c++11中的右值引用,如果想要引用左值,c++11提供了std::move(左值对象)
    cout << "---------------------右值引用,引用左值----------------" << endl;
    A&& a4=move(a3);
    a4.showInfo();
    return 0;
}

拓展:

(N)RVO(Return Value Optimization),是一种编译器优化技术,通过该技术,编译器可以减少函数返回时生成临时对象的个数,从某种程度上可以提高程序的运行效率,对需要分配大量内存的类对象其值复制过程十分友好。
当一个未具名且未绑定到任何引用的临时变量被移动或复制到一个相同的对象时,拷贝和移动构造可以被省略。当这个临时对象在被构造的时候,他会直接被构造在将要拷贝/移动到的对象。当未命名临时对象是函数返回值时,发生的省略拷贝的行为被称为RVO(返回值优化)。
RVO优化针对的是返回一个未具名对象或N(具名对象),也就是说(N)RVO的功能是消除函数返回时创建的临时对象。
编译器会把存储这个局部对象的地址和存储返回值临时对象的地址进行复用,也就是说避免了从局部对象到临时对象的拷贝操作,这就是RVO。
可以通过-fno-elide-constructors来禁用RVO。

移动构造函数

是深拷贝的强有力的补充

逻辑转移资源

使用条件原对象不在使用时,移动到新的空间

#include <iostream>

using namespace std;

class A{
    int *p;
public:
    A(){
        this->p=new int[1024];
        cout << "A structure" << endl;
    }
    ~A(){
        if(nullptr==this->p){
            delete []p;
        }
        cout << "A destruct" << endl;
    }
    A(const A& other){
        //升级为深拷贝
        this->p=new int[1024];
        memcpy(this->p,other.p,sizeof (int[1024]));
        cout << "A copy" << endl;
    }
    //c++11提供了移动构造,避免空间浪费
    A(A&& other){
        this->p=other.p;
        other.p=nullptr;
        cout << "A move constructor" << endl;
    }
    void showInfo(){
        cout << "hello" << endl;
    }
};
int main()
{
    A a1;
    //此时放生移动构造
//    A a2=a1;//此时出现两份,浪费空间
    A a2=std::move(a1);
    a1.showInfo();
    return 0;
}

c++异常处理机制

c中异常处理存在问题:

#include <iostream>

using namespace std;

float my_div(float a,float b){
    if(b==0){
        return -1;
    }
    return a/b;
}
float my_add(float a,float b){
    if(my_div(a,b)==-1){
        return -1;
    }
    return a+b+my_div(a,b);
}
int main()
{
    float a=10,b=-10;
    cout << my_add(a,b) << endl;//-1
    return 0;
}

c++中处理机制:

throw抛出异常关键字

#include <iostream>

using namespace std;

float my_div(float a,float b){
    if(b==0){
        throw -1;
    }
    return a/b;
}
float my_add(float a,float b){
    return a+b+my_div(a,b);
}
int main()
{
    float a=10,b=-10;
    cout << my_add(a,0) << endl;
    return 0;
}

在这里插入图片描述

try{捕获throw抛出异常}catch(表达式){处理异常}语句捕获异常,并处理。

#include <iostream>
#include <exception>

using namespace std;

class Error:public exception{
private:
    string error;
public:
    Error(string error){
        this->error=error;
    }
    //封装一个what函数
    const char * what() const noexcept override{
        return this->error.c_str();
    }
};
float my_div(float a,float b){
    if(b==0){
        throw logic_error("除数不可为0");
        //throw -1;
    }
    if(-10==b){
        throw "嘛呢,大哥" ;
    }
    if(88==b){
        throw Error("好好学习");
    }
    return a/b;
}
float my_add(float a,float b){
    return a+b+my_div(a,b);
}
int main()
{
    float a=10,b=-10;
    try {
        //一旦出现异常,就要跳转到catch,所以try...catch不能分开
        cout << my_add(a,88) << endl;
        //...下面语句不会被执行
        cout << "hello" << endl;
    } catch (exception& error) {//多态
        //catch (int error)
        cout << error.what() << endl;
        //cout << "除数不可为0" << endl;
    }catch(const char* c_char){
        cout << c_char << endl;
    }catch(Error& error){
        cout << error.what() << endl;
    }
    //这句话正常执行
    cout << "11111" << endl;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/64681.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring导入第三方资源

一、第三方资源配置管理 说明&#xff1a;以管理DataSource连接池对象为例讲解第三方资源配置管理 1 管理DataSource连接池对象 问题导入 配置数据库连接参数时&#xff0c;注入驱动类名是用driverClassName还是driver&#xff1f; 1.1 管理Druid连接池【重点】 数据库准备…

Android挂载系统分区执行mount和remount

第一种&#xff0c;通过adb挂载&#xff0c;需要root权限&#xff1b; adb root //adb 获取root权限&#xff1b; adb remount /system //从新挂载system目录或者说分区 第二种&#xff1a;通过Android下的Linux命令挂载&#xff0c;需要root权限&#xff1b; adb shell //…

【第八章 索引,索引结构,B-Tree,B+Tree,Hash,索引分类,聚集索引二级索引,索引语法】

第八章 索引&#xff0c;索引结构&#xff0c;B-Tree&#xff0c;BTree&#xff0c;Hash&#xff0c;索引分类&#xff0c;聚集索引&二级索引&#xff0c;索引语法 1.索引&#xff1a; ①索引&#xff08;index&#xff09;是帮助MySQL高效获取数据的数据结构(有序)。 ②特…

[附源码]Python计算机毕业设计Django社区人员信息管理系统设计与实现

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Java学习笔记——StringBuilder

目录StringBuilder概述StringBuilder的构造方法StringBuilder常用的成员方法StringBuilder提高效率的原因String和StringBuilder之间的转换StringBuilder概述 StringBuilder是一个可变字符串类&#xff0c;我们可以把它看成一个容器。 作用&#xff1a;提高字符串的操作效率 …

HTML5期末作业:明星网站设计与实现——明星薛之谦介绍网页设计7个页面HTML+CSS+JavaScript

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

一文叫你使用trace查看函数调用关系|分析Linux性能

抓取函数调用流程关系 抓取函数耗时 抓取代码片耗时 抓取函数里每个子函数时间戳 抓取事件信息 trace是内核自带的工具&#xff0c;相比于perf工具&#xff0c;trace只管抓trace数据并没有分析&#xff0c;perf在trace数据分析方面做出了很多成果。 但是我们现在就想看一下…

java生产者 消费者模式概念讲解

首先我们要了解生成者和消费者的基本概念 我们举个小例子 图里是一个奶箱 但里面并没有牛奶 我们想喝也没用 然后 送奶的工人送来了几瓶牛奶 然后工人会把牛奶放进奶箱 然后 把箱子给你锁上 然后你就可以去奶箱里拿奶来喝 这里 我们就可以比喻成 奶箱是一个存储数据的容器…

nginx安装与使用(入门)

目录 安装 入门 1. 什么是nginx 2. Nginx的作用 部署静态资源到ngnix 配置文件 部署外部资源到nginx 多个虚拟主机配置与域名映射 配置多个虚拟主机 配置反向代理 安装 win下 前往这里下载 nginx: download 如果启动后报错&#xff1a; nginx: [emerg] bind() to 0.0…

[附源码]Python计算机毕业设计Django甜品购物网站

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【数据库与事务系列】分库分表中间件

前面讲了利用mybatis插件进行多数据源切换和分表的方案&#xff0c;但是对业务侵入性较强&#xff0c;当然给予mybatis-plus的对业务侵入性还好&#xff0c;但是支持的策略有限、场景有限。 所以业界诞生了很多分库分表中间件来专门解决这些问题。我们先来看看分库分表的背景。…

初始计算机网络——概念、组成、功能、分类

大一下学期学习计算机网络&#xff0c;但是那个时候很傻&#xff0c;不知道计算机网络的重要性&#xff0c;现在知道了只能回过头来补充知识&#xff0c;这个计算机网络真的很重要&#xff0c;也很后悔当时没有好好学 目录 一、计算机网络的概念 1.1 网络与计算机网络 1.2 计…

校园论坛(Java)—— 校园周边模块

校园论坛&#xff08;Java&#xff09;—— 校园周边模块 文章目录校园论坛&#xff08;Java&#xff09;—— 校园周边模块1、写在前面2、系统结构设计2.1 各个页面之间的调用关系2.2 校园周边页面设计3、校园周边模块设计3.1 校园周边主界面的实现3.2 增加附近的交通信息4. 总…

Vue v-model收集表单数据

Vue v-model收集表单数据1 单行文本2 多行文本3 复选框4 单选按钮5 选择器6 修饰符1 单行文本 对于输入框input&#xff0c;直接使用v-model绑定属性即可&#xff0c;该属性需要在data中定义。用法如下&#xff1a; <input v-model"content">var vm new Vue…

力扣 394. 字符串解码

题目 给定一个经过编码的字符串&#xff0c;返回它解码后的字符串。 编码规则为: k[encoded_string]&#xff0c;表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 你可以认为输入字符串总是有效的&#xff1b;输入字符串中没有额外的空格&#x…

快速上手Django(六) -Django之Django drf 序列化器Serializer类

文章目录快速上手Django(六) -Django之Django drf 序列化器Serializer类1. 背景2. 使用思路3. 代码demo4. 【重要】序列化类 ModelSerializer5. DRF序列化器to_representation()和to_internal_value()方法to_representation如何使用示例&#xff1a;例如处理返回的日期格式二、…

Habor私有仓库

Harbor简介   Harbor是一个用于存储和分发Docker镜像的企业级Registry的服务器镜像仓库&#xff0c;通过添加一些企业必需的功能特性&#xff0c;例如安全、标识和管理等&#xff0c;扩展了开源Docker Distribution。作为一个企业级私有Registry服务器&#xff0c;Harbor提供…

Python基础知识进阶之正则表达式

一.match re.match 是从字符串的起始位置匹配一个模式&#xff0c; 匹配成功 re.match 方法返回一个匹配的对象&#xff0c;否则返回 None。 re.match(pattern, string, flags0) pattern&#xff1a; 匹配的正则表达式。 string&#xff1a; 要匹配的字符串。 flags&…

LeetCode刷题复盘笔记—一文搞懂完全背包之322. 零钱兑换问题(动态规划系列第十四篇)

今日主要总结一下动态规划完全背包的一道题目&#xff0c;322. 零钱兑换 题目&#xff1a;322. 零钱兑换 Leetcode题目地址 题目描述&#xff1a; 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返…

MATLAB绘制伪彩图和切片轮廓线图

n6%定义轮数 r(0:n)/n;%定义轮的半径 thetapi*(-n:n)/n;%定义轮的扇区角 Xr*cos(theta); Yr*sin(theta);%定义网格顶点 Cr*cos(2*theta);%定义色图 pcolor(X,Y,C)%绘制伪彩图axis equal tight[x y z v]flow;%打开水流数据 hcontourslice(x,y,z,v,[1:9],[],[0],linspace(-8,2,10…