9/3作业

news2024/9/19 10:46:04

一、继承(inhert)

面向对象三大特征:封装、继承、多态

继承:所谓继承,是类与类之间的关系。就是基于一个已有的类,来创建出一个新类的过程叫做继承。主要提高代码的复用性。

1.1 继承的作用

1> 实现代码的复用性

2> 继承是实现多态的重要基础,没有继承就没有多态

1.2 一个类B继承自类A

此时我们称A类为父类、基类。B类为子类、派生类

1.3 继承的格式

class 子类名 :继承方式  class 父类名1, class 父类2。。。
{
    子类扩展的成员;
};

1.4 继承方式

1> 继承方式一共有三种:public、protected、private

2> 回顾访问权限

                   类内              子类            类外
public             !                   !                  !
protected        !                   !                 X
private            !                   X                X

3> 继承方式时父类中的所有访问权限在子类中的表现形式

父类中            public|protected|private|不可访问            public|protected|private|不可访问             public|protected|private|不可访问 
继承方式                    public                                        protected                                        private
子类中            public|protected|不可访问|不可访问            protected|protected|不可访问|不可访问         private|private|不可访问|不可访问 

总结:继承方式是父类成员的权限在子类中的最高访问权限

4> 访继承方式也可以省略不写,如果省略不写,则默认的继承方式为私有的(private)

5> 常用的继承方式是:public

#include <iostream>


using namespace std;


//定义父类
class Person
{
public:
    string name = "张三";        //公共成员姓名
protected:
    int pwd = 666666;            //受保护成员 银行卡密码
private:
    int money = 888888;           //私有成员 私房钱


public:
    Person() {}
    Person(string n, int p, int m):name(n),pwd(p),money(m) {}
    ~Person() {}


    void show()
    {
        cout<<"name = "<<name<<endl;         //父类中的公共权限下的成员,父类中可以访问
        cout<<"pwd = "<<pwd<<endl;         //父类中的受保护权限下的成员,父类中可以访问
        cout<<"money = "<<money<<endl;         //父类中的私有权限下的成员,父类中可以访问
    }
};


//基于一个已有的父类,定义一个新类
class Stu : public Person        //公共继承
//class Stu : protected Person       //受保护继承
//class Stu :  Person       //私有继承
{
public:
    string sex = "男";         //性别
protected:
    int score = 99;          //受保护成员分数
private:
    int age = 18;            //私有成员 年龄


public:
    Stu() {}
    ~Stu() {}


    //定义show函数
    void show()
    {
        cout<<"name = "<<name<<endl;         //从父类中继承的的公共权限下的成员,子类中可以访问
        cout<<"pwd = "<<pwd<<endl;         //从父类中继承的受保护权限下的成员,子类中可以访问
        //cout<<"money = "<<money<<endl;         //从父类中继承的私有权限下的成员,子类中不可以访问
        cout<<"sex = "<<sex<<endl;            //子类中的公共权限下的成员,子类中可以访问
        cout<<"score = "<<score<<endl;            //子类中的受保护权限下的成员,子类中可以访问
        cout<<"age = "<<age<<endl;            //子类中的私有权限下的成员,子类中可以访问
    }




};

int main()
{
    cout << sizeof (Person) << endl;
    cout << sizeof (Stu) << endl;


    //使用子类实例化对象
    Stu s1;
    //s1.show();


    cout<<"name = "<<s1.name<<endl;           //继承的公共权限下的成员,子类中也是公共的,类外可以访问
    //cout<<"pwd = "<<s1.pwd<<endl;               //继承的受保护的权限下成员,子类中也是受保护的,类外无法访问
    //cout<<"money = "<<s1.money<<endl;          //继承的私有成员,子类中无法访问,类外也无法访问
    cout<<"sex = "<<s1.sex<<endl;               //子类中的公共权限下的成员类外可以访问
    //cout<<"score = "<<s1.score<<endl;            //子类受保护的成员,类外无法访问
    //cout<<"age = "<<s1.age<<endl;                 //子类的私有成员,类外无法访问


    return 0;
}

1.5 继承过程中的成员

1> 子类会继承父类中的所有成员,包括私有成员,只不过不能子类中不能使用父类的私有成员。如果非要使用父类的私有成员,需要在父类中提供public或者protected类型的接口函数完成对私有成员的操作。

2> 想要完成对 子类从父类中继承下来的成员的初始化工作,需要在子类的初始化列表中,显性调用父类的构造函数来完成。如果没有显性调用父类的有参构造,那么系统会自动调用父类的无参构造,来完成对继承下来的成员的初始化工作。

在这个过程中,虽然调用的父类的构造函数,但是并没有实例化父类对象,最终对象的个数只有一个

3> 类与类之间的关系模型

1、继承关系:is a 模型,是特殊的包含关系(has a模型)

2、包含关系:has a模型,在一个类中,有另一个类的成员子对象

3、友元关系:use a模型,在一个类中,使用另一类中的内容

#include <iostream>


using namespace std;


//定义父类
class Person
{
public:
    string name = "张三";        //公共成员姓名
protected:
    int pwd = 666666;            //受保护成员 银行卡密码
private:
    int money = 888888;           //私有成员 私房钱


public:
    Person() {cout<<"Person::无参构造"<<endl;}
    Person(string n, int p, int m):name(n),pwd(p),money(m) {cout<<"Person::有参构造"<<endl;}
    ~Person() {cout<<"Person::析构函数"<<endl;}


    void show()
    {
        cout<<"name = "<<name<<endl;         //父类中的公共权限下的成员,父类中可以访问
        cout<<"pwd = "<<pwd<<endl;         //父类中的受保护权限下的成员,父类中可以访问
        cout<<"money = "<<money<<endl;         //父类中的私有权限下的成员,父类中可以访问
    }
};


//基于一个已有的父类,定义一个新类
class Stu : public Person        //公共继承
//class Stu : protected Person       //受保护继承
//class Stu :  Person       //私有继承
{
public:
    string sex = "男";         //性别
protected:
    int score = 99;          //受保护成员分数
private:
    int age = 18;            //私有成员 年龄


public:
    Stu() {cout<<"Stu::无参构造"<<endl;}
    Stu(string n, int p, int m,string s, int s1, int a):sex(s), score(s1), age(a) {cout<<"Stu::有参构造"<<endl;}
    ~Stu() {cout<<"Stu::析构函数"<<endl;}


    //定义show函数
    void show()
    {
        cout<<"name = "<<name<<endl;         //从父类中继承的的公共权限下的成员,子类中可以访问
        cout<<"pwd = "<<pwd<<endl;         //从父类中继承的受保护权限下的成员,子类中可以访问
        //cout<<"money = "<<money<<endl;         //从父类中继承的私有权限下的成员,子类中不可以访问
        cout<<"sex = "<<sex<<endl;            //子类中的公共权限下的成员,子类中可以访问
        cout<<"score = "<<score<<endl;            //子类中的受保护权限下的成员,子类中可以访问
        cout<<"age = "<<age<<endl;            //子类中的私有权限下的成员,子类中可以访问
    }

};

int main()
{
    cout << sizeof (Person) << endl;
    cout << sizeof (Stu) << endl;


    //使用子类实例化对象
    Stu s1("zhangpp", 111111, 100, "男", 99, 18);    //有参构造
    //s1.show();


    cout<<"name = "<<s1.name<<endl;           //继承的公共权限下的成员,子类中也是公共的,类外可以访问
    //cout<<"pwd = "<<s1.pwd<<endl;               //继承的受保护的权限下成员,子类中也是受保护的,类外无法访问
    //cout<<"money = "<<s1.money<<endl;          //继承的私有成员,子类中无法访问,类外也无法访问
    cout<<"sex = "<<s1.sex<<endl;               //子类中的公共权限下的成员类外可以访问
    //cout<<"score = "<<s1.score<<endl;            //子类受保护的成员,类外无法访问
    //cout<<"age = "<<s1.age<<endl;                 //子类的私有成员,类外无法访问


    cout<<"**********************************************"<<endl;
    Person p1;
    cout<<"&p1 = "<<&p1<<"     &p1.name = "<<&p1.name<<endl;
    cout<<"**********************************************"<<endl;


    cout<<"&s1 = "<<&s1<<"     &s1.name = "<<&s1.sex<<endl;
    cout<<"**********************************************"<<endl;


    return 0;
}

1.6 子类继承父类的过程

1> 继承步骤

1、全盘吸收父类的内容:子类会继承父类的所有成员,包括私有成员

2、更改父类中的成员属性(using):子类可以更改从父类中继承下来的能够访问的成员的权限

3、拓展新成员:子类也可以定义独属于子类自己的成员

2> 子类中可以定义与父类中同名的相关成员,如果通过子类对象进行调用时,默认使用的是子类的成员,如果非要使用父类中的成员,需要使用父类名和作用域限定符来找到父类的内容进行访问

#include <iostream>


using namespace std;


class Father
{
public:
    int num;
protected:
    int value;
private:
    int key;


public:
    Father() {}
    Father(int n, int v, int k):num(n), value(v), key(k) {}
    ~Father() {}
    void show()
    {
        cout<<"Father::num = "<<num<<endl;
        cout<<"Father::value = "<<value<<endl;
        cout<<"Father::key = "<<key<<endl;
    }
};


//子类继承父类
class Son:public Father
{
    //子类是可以更改继承下来的父类中能访问的成员的
public:
    using Father::value;       //把从父类中继承下来的value成员变量的权限由protected改成public
    string name;                  //子类中拓展的成员

private:
    using Father::num;          //将从父类中继承下来的num的权限由public改成private


public:
    Son(){}
    Son(int n, int v, int k, string name):Father(n,v,k), name(name){}
    ~Son(){}


    void show()
    {
        cout<<"Son::num = "<<num<<endl;
        cout<<"Son::value = "<<value<<endl;
        //cout<<"Son::key = "<<key<<endl;
        cout<<"Son::name = "<<name<<endl;
    }


};






int main()
{
    Son s(520, 1313, 666, "zhasngpp");
    s.show();                  //默认调用的是自己的成员函数
    cout<<"**********************************"<<endl;
    s.Father::show();           //调用父类提供的同名的函数
    cout<<"**********************************"<<endl;
    s.Father::num = 888;
    s.show();                  //子类和父类中使用的num是同一个
    cout<<"**********************************"<<endl;


    return 0;
}

1.7 继承过程中的特殊成员函数

1> 构造函数:父子类中拥有不同的构造函数,在构造子类时,需要显性调用父类的有参构造完成对子类从父类中继承下来成员的初始化工作,如果没有显性调用,系统会自动调用父类的无参构造完成对成员的初始化工作

构造顺序:先构造父类再构造子类

2> 析构函数:父子类中拥有不同的析构函数,在析构子类时,不需要显性调用父类的析构函数,系统会自动调用父类的析构函数,来完成对子类从父类中继承下来成员的空间回收

析构顺序:先析构子类再析构父类

3> 拷贝构造函数:父子类中拥有不同的拷贝构造函数,需要在子类的拷贝构造函数的初始化列表中显性调用父类的拷贝构造函数来完成对子类从父类继承下来成员的初始化工作,如果没有显性调用父类的拷贝构造函数,那么系统会自动调用父类的无参构造来完成对那些成员的初始化工作。

4> 拷贝赋值函数:父子类中拥有不同的拷贝赋值函数,需要在子类的拷贝赋值函数的函数体内,显性调用父类的拷贝赋值函数来完成对子类从父类中继承的成员的赋值工作,如果没有显性调用父类的拷贝赋值函数,那么系统不会自动调用其他函数。当前对象中父类从子类中继承下来的成员的值保持不变。

#include <iostream>


using namespace std;


class Father
{
public:
    int num;
protected:
    int value;
private:
    int key;


public:
    Father() {cout<<"Father::无参构造"<<endl;}
    Father(int n, int v, int k):num(n), value(v), key(k) {cout<<"Father::有参构造"<<endl;}
    ~Father() {cout<<"Father::析构函数"<<endl;}
    Father(const Father&other):num(other.num), value(other.value), key(other.key)
    {cout<<"Father::拷贝构造"<<endl;}
    //拷贝赋值函数
    Father &operator=(const Father&other)
    {
        if(this!=&other)
        {
            this->num = other.num;
            this->key = other.key;
            this->value = other.value;
        }
        cout<<"Father::拷贝赋值函数"<<endl;
        return *this;
    }
    void show()
    {
        cout<<"Father::num = "<<num<<endl;
        cout<<"Father::value = "<<value<<endl;
        cout<<"Father::key = "<<key<<endl;
    }
};


//子类继承父类
class Son:public Father
{
    //子类是可以更改继承下来的父类中能访问的成员的
public:
    using Father::value;       //把从父类中继承下来的value成员变量的权限由protected改成public
    string name;                  //子类中拓展的成员


private:
    using Father::num;          //将从父类中继承下来的num的权限由public改成private


public:
    Son(){cout<<"Son::无参构造"<<endl;}
    Son(int n, int v, int k, string name):Father(n,v,k), name(name){cout<<"Son::有参构造"<<endl;}
    ~Son(){cout<<"Son::析构函数"<<endl;}
    Son(const Son&other):Father(other),name(other.name)
    {cout<<"Son::拷贝构造"<<endl;}
    //定义子类的拷贝赋值函数
    Son &operator=(const Son&other)
    {
        if(this != &other)
        {
            //显性调用父类的拷贝赋值函数
            this->Father::operator=(other);
            this->name = other.name;
        }
        cout<<"Son::拷贝赋值函数"<<endl;
        return *this;


    }




    void show()
    {
        cout<<"Son::num = "<<num<<endl;
        cout<<"Son::value = "<<value<<endl;
        //cout<<"Son::key = "<<key<<endl;
        cout<<"Son::name = "<<name<<endl;
    }


};

int main()
{
    {
        Son s(520, 1313, 666, "zhasngpp");
        s.show();                  //默认调用的是自己的成员函数
        cout<<"**********************************"<<endl;
        s.Father::show();           //调用父类提供的同名的函数
        cout<<"**********************************"<<endl;
        s.Father::num = 888;
        s.show();                  //子类和父类中使用的num是同一个
        cout<<"**********************************"<<endl;
        Son s1 = s;
        s1.show();
        cout<<"**********************************"<<endl;
        Son s2;         //无参构造
        cout<<"**********************************"<<endl;
        s2 = s1;        //拷贝构造
        s2.show();
        cout<<"**********************************"<<endl;


    }
    cout<<"**********************************"<<endl;


    return 0;
}

1.8 多级继承

1> C++中允许多级继承,子类继承自父类,孙子类继承自子类

2> 当前子类会用于祖先类的所有成员,包括私有成员

1.9 多重继承

1> C++的面向对象是支持多重继承的

2> 允许一个子类由多个父类共同派生出来,当前子类会继承所有父类的成员

3> 继承格式

class  子类名:继承方式1  父类1, 继承方式2 父类2,。。。,继承方式n 父类n
{
    子类拓展成员
}

4> 需要在子类的构造函数的初始化列表中,显性调用所有父类的构造函数来完成对从不同父类中继承下来成员的初始化工作,如果没有显性调用有参构造,系统会自动调用对应父类的无参构造完成初始化工作

5> 子类中调用父类的构造函数的顺序跟继承顺序有关,跟构造函数的摆放顺序无关

6> 产生问题:多个父类中可能会出现同名的成员,子类对象访问起来就会产生歧义。

解决办法:需要使用对应的父类名和作用域限定符来指定

#include <iostream>


using namespace std;


class A
{
public:
    string name;
    int value_a;
public:
    A() {}
    A(string n, int va):name(n), value_a(va) {cout<<"A::有参构造"<<endl;}


};


class B
{
public:
    string name;
    int value_b;
public:
    B() {}
    B(string n, int vb):name(n), value_b(vb) {cout<<"B::有参构造"<<endl;}
};


//定义子类继承自上面两个类
class C:public A, public B
{
public:
    C(){}
    C( string n2, int vb,string n1, int va):A(n1, va),B(n2,vb){cout<<"C::有参构造"<<endl;}
};






int main()
{
    C c1("zhangs",520, "李四",1314);
    //cout<<c1.name<<endl;               //访问由多个父类继承下来的同名成员时,容易产生歧义
    cout<<c1.A::name<<endl;              //可以在成员名前加父类名和作用域限定符
    cout<<c1.B::name<<endl;


    return 0;
}

1.10 菱形继承问题

1> 问题模型

                                A                     公共基类
                             /     \
                           B       C               中间子类
                              \    /
                                D                     汇聚子类

2> 问题阐述

中间子类会正常继承公共基类的所有成员,但是,使用多个中间子类共同派生出一个汇聚子类时,该汇聚子类中,就会同时拥有多份公共基类的成员,会造成汇聚子类类体膨胀,访问数据也比较麻烦。这个问题成为菱形基础问题也称钻石继承。

1.11 虚继承

1> 虚继承是为了解决菱形继承问题引入的

2> 继承格式:在生成中间子类时,在继承方式前加关键字 virtual ,那么该继承方式就是虚继承

3> 汇聚子类中,仅仅只保留一份公共基类的成员

4> 由于传递给公共基类的数据只有一份,但是有多个父类的构造函数。原则上来说,从父类中继承的成员需要调用直接父类来进行初始化操作,但是这一份成员,不能确定是由哪一个父类来进行构造。索性,直接父类的构造函数全部都不用,直接由公共基类进行构造。

5> 需要在汇聚子类的构造函数初始化列表中,显性调用公共基类的有参构造,来完成对汇聚子类中从公共基类中继承的成员的初始化工作,如果没有显性调用公共基类的有参构造,则系统会自动调用公共基类的无参构造来完成。

#include <iostream>


using namespace std;


class A
{
protected:
    int value;
public:
    A() {cout<<"A::无参构造"<<endl;}
    A(int v): value(v) {cout<<"A::有参构造"<<endl;}
    ~A() {cout<<"A::析构函数"<<endl;}
};


//生成中间子类:使用的是虚继承
class B:virtual public A
{
protected:
    int value_b;
public:
    B() {cout<<"B::无参构造"<<endl;}
    B(int v, int b):A(v), value_b(b) {cout<<"B::有参构造"<<endl;}
    ~B() {cout<<"B::析构函数"<<endl;}
};


class C:virtual public A
{
protected:
    int value_c;
public:
    C() {cout<<"C::无参构造"<<endl;}
    C(int v, int c):A(v), value_c(c) {cout<<"C::有参构造"<<endl;}
    ~C() {cout<<"C::析构函数"<<endl;}
};


//通过中间子类,共同生成汇聚子类
class D:public B, public C
{
protected:
    int value_d;
public:
    D() {cout<<"D::无参构造"<<endl;}
    D(int a1, int a2, int b, int c, int d):A(520), B(a1,b),C(a2,c), value_d(d) {cout<<"D::有参构造"<<endl;}
    ~D() {cout<<"D::析构函数"<<endl;}


    void show()
    {
        cout<<"value = "<<value<<endl;          //数据只有一份
        cout<<"value = "<<B::value<<"   &value = "<<&(B::value)<<endl;        //说明是从B中传下来的value
        cout<<"value = "<<C::value<<"   &value = "<<&(C::value)<<endl;        //说明从C中传下来的value
        cout<<"value_b = "<<value_b<<endl;
        cout<<"value_c = "<<value_c<<endl;
        cout<<"value_d = "<<value_d<<endl;
    }
};






int main()
{
    //cout << sizeof(D) << endl;
    D d1(1,2,3,4,5);
    d1.show();
    return 0;
}

二、多态

面向对象的三大特征:封装、继承、多态

多态:就是多种状态,能够实现“一物多用”,是实现泛型编程的重要途径

2.1 多态的实现

父类的指针或引用可以指向子类的对象,进而调用子类中重写的父类的虚函数。

2.2 函数重写(override)

1> 函数重写是发生在父子类中

2> 要求在子类中定义与父类中函数原型相同的函数

原型相同:返回值类型、函数名、参数列表都相同

2.3 虚函数

1> 定义格式:在定义函数前加关键字 virtual,此时的函数就是虚函数

2> 需要在父类中定义虚函数,子类中可以进行重写也可以不重写

3> 当一个类中,如果定义了虚函数,那么该类会增加一个指针的大小,这个指针指向虚函数表

4> 如果一个类中的某个函数定义成虚函数,那么该类的子子孙孙类中的该函数都是虚函数,即使没有加virtual

#include <iostream>


using namespace std;


class Animal
{
public:
    string name;      //名称
public:
    Animal() {}
    Animal(string n):name(n) {}
    ~Animal() {}


    //定义虚函数
    virtual void voice()
    {
        cout<<"~~~~~~~~"<<endl;
    }
};


//定义羊类
class Sheep:public Animal
{
public:
    int leg;       //腿的个数
public:
    Sheep(){}
    Sheep(string n, int l):Animal(n), leg(l){}
    ~Sheep(){}


    //重写父类的函数
    void voice() override
    {
        cout<<"咩咩咩~~~~~"<<endl;
    }


    //定义子类自己的函数
    void show()
    {
        cout<<"我是:"<<name<<"   有"<<leg<<"条腿"<<endl;
    }


};






int main()
{
    Sheep s1("喜羊羊", 2);
    s1.voice();            //调用的是自己的
    s1.Animal::voice();      //调用父类的
    s1.show();


    //定义一个父类的引用,目标为子类
    Animal &r1 = s1;
    r1.voice();               //调用父类的
    //r1.show();

    return 0;
}

2.4 使用多态实现的实例

#include <iostream>


using namespace std;


class Hero
{
public:
    string name;
    int attack = 10;        //基础攻击力
    static int boss_blood ;  //暴君的血量
public:
    Hero() {}
    Hero(string n, int a):name(n), attack(a) {}
    ~Hero() {}


    //在父类中定义虚函数
    virtual void jungle()
    {
        boss_blood -= attack;     //每攻击一次野怪就掉血
    }
};


int Hero::boss_blood = 1000;      //初始血量


//定义具体英雄类
class Assassin:public Hero
{
public:
    int speed ;      //移速加成
public:
    Assassin(){}
    Assassin(string n, int a, int s):Hero(n, a+50), speed(s){}
    ~Assassin(){}


    //重写子父类的虚函数
    void jungle()
    {
        boss_blood -= attack;     //每攻击一次野怪就掉血
    }


};


//定义具体英雄类
class Master:public Hero
{
public:
    int speed ;      //移速加成
public:
    Master(){}
    Master(string n, int a, int s):Hero(n, a+5), speed(s){}
    ~Master(){}


    //重写父类的虚函数
    void jungle()
    {
        boss_blood -= attack;     //每攻击一次野怪就掉血
    }
};


//功能函数完成打野功能
void fun(Hero &hero)
{
    hero.jungle();
    cout<<hero.name<<"攻击了暴君,目前暴君血量为:"<<hero.boss_blood<<endl;
}


int main()
{
    Assassin h1("李白", 70, 300);    //实例化刺客


    Master h2("妲己", 30, 250);         //实例化法师


    fun(h1);
    fun(h2);




    return 0;
}

2.5 虚函数的底层实现

2.6 重载(voerload)、重写(override)和隐藏(hide)的区别

1> 函数重载:函数名相同,形参列表必须不同

1、作用域相同

2、函数名相同

3、参数列表必须不同(个数、类型)

4、有无 virtual 都无所谓

5、跟返回值没有关系

2> 函数重写:子类中重写父类的虚函数

1、作用域发生在父子类中

2、函数原型相同(返回值、参数个数、参数类型、函数名)

3、父类中的函数必须要有 virtual 关键字

3> 函数隐藏:子类中定义与父类同名的函数

1、作用域发生在父子俩中

2、函数名相同

3、返回值可以不同

4、参数相同

4、没有virtual修饰

2.7 纯虚函数(抽象类)

1> 对于有些类而言,类中的相关成员函数没有实现的意义,主要是让子类来完成重写操作的

2> 以便于使用父类的指针或引用指向子类对象,调用子类中重写的父类的虚函数

3> 我们就可以将这样的函数设置成纯虚函数

4> 定义格式: virtual 返回值类型 函数名(形参列表) = 0;

5> 要求子类中必须对这些纯虚函数进行重写

6> 抽象类:包含纯虚函数的类叫做抽象类,抽象类是不能实例化对象的

7> 如果包含纯虚函数的子类中没有重写其虚函数,那么其子类也是抽象类,子类中的该函数也还是纯虚函数

#include <iostream>


using namespace std;


class shape
{
public:
   double perimeter;
   double area;


public:
virtual void output() = 0;


};




class Circle:public shape
{
private:
    double radius;
public:
    Circle():radius(0){}
    Circle(double r):radius(r){}
    ~Circle(){}
    void output()
    {
        cout<<"周长="<<2*radius<<"pi"<<endl;
        cout<<"面积="<<radius*radius<<"pi"<<endl;
    }
};




class Rectangle:public shape
{
private:
    double width;
    double height;
public:
    Rectangle():width(0),height(0){}
    Rectangle(double w,double h):width(w),height(h){}
    ~Rectangle(){}
    void output()
    {
        cout<<"周长="<<2*(width+height)<<endl;
        cout<<"面积="<<width*height<<endl;
    }
};




int main()
{
    //shape s;                //抽象类不能实例化对象
    Circle c1(2.5);
    Rectangle r1(3.5,4.2);


    //定义父类指针
    shape *ptr = &c1;       //定义父类指针指向子类对象
    ptr->output();           //父类?子类?
    cout<<"************************"<<endl;
    //ptr->shape::output();






    return 0;
}

2.8 虚析构函数

1> 当使用父类指针指向子类对象时,构造时会正常先构造父类后构造子类,但是在使用delete释放内存空间时,由于父类指针的作用域,只作用在子类的父类空间内,所以,只会调用父类的析构函数,子类自己的空间就泄露了

2> 此时可以使用虚析构函数来解决:定义析构函数时,在函数头前面加关键字virtual即可

3> 虚析构函数能正确引导delete关键字,在释放父类空间时,把子类的空间一并释放

4> 如果父类的析构函数为虚析构函数,那么该类的子子孙孙类中的析构函数都是虚析构函数

#include <iostream>


using namespace std;


class shape
{
public:
   double perimeter;
   double area;




public:
shape(){cout<<"shape ::构造函数"<<endl;}
virtual ~shape(){cout<<"shape ::析构函数"<<endl;}         //定义虚析构函数
virtual void output() = 0;


};




class Circle:public shape
{
private:
    double radius;
public:
    Circle():radius(0){}
    Circle(double r):shape(),radius(r){}
    ~Circle(){}
    void output()
    {
        cout<<"周长="<<2*radius<<"pi"<<endl;
        cout<<"面积="<<radius*radius<<"pi"<<endl;
    }
};




class Rectangle:public shape
{
private:
    double width;
    double height;
public:
    Rectangle():width(0),height(0){}
    Rectangle(double w,double h):shape(),width(w),height(h){cout<<"rectangle::构造函数"<<endl;}
    ~Rectangle(){cout<<"rectangle::析构函数"<<endl;}
    void output()
    {
        cout<<"周长="<<2*(width+height)<<endl;
        cout<<"面积="<<width*height<<endl;
    }
};




int main()
{
    shape *ptr = new Rectangle(3,5);         //在堆区申请一个子类的对象,用父类的指针指向
    ptr->output();         //正常输出
    delete ptr;






    return 0;
}

作业:

#include <iostream>

using namespace std;
class mystack
{
private:
    char *ptr;  //
    int num;    //栈的成员个数
    int max;    //栈的最大容量
public:
    mystack():num(0),max(10)
    {
        ptr=new char[max];
    }
    mystack(const char *s):num(0),max(10)
    {
        ptr=new char[max];
        for(int i=0;*(s+i)!=0;i++)
        {
            expend();
            *(ptr+i)=*(s+i);
            num++;
        }
    }
    ~mystack(){}
    mystack operator =(const mystack &other)
    {
        if(this!= &other)
        {
            delete ptr;
            ptr=new char[other.max];
            this->num=other.num;
            for(int i=0;i<num;i++)
            {
                *(ptr+i)=*(other.ptr+i);
            }

        }
        return *this;
    }

    char top()
    {
        return *(ptr+num);
    }

    bool empty()
    {
        return num==0;
    }

    int size()
    {
        return num;
    }

    void expend()
    {
        //如果为满,则扩容
        if(num==max)
        {
            char *data;
            max=max*2;
            data=new char[max];
            memcpy(data,ptr,max/2);
            delete ptr;
            ptr=data;
        }

        return;
    }
    void push(char n)
    {
        expend();
        *(ptr+num)=n;
        num++;
    }

    char pop()
    {
        return *(ptr+num--);
    }
};


int main()
{
    mystack s1="abcdefg";

    cout<<s1.size()<<endl;

    return 0;
}
#include <iostream>

using namespace std;
#include <iostream>

using namespace std;
class myqueue
{
private:
    char *ptr;  //
    int head;   //头索引
    int rear;   //尾索引
    int num;    //栈的成员个数
    int max;    //栈的最大容量
public:
    myqueue():head(0),rear(0),num(0),max(10)
    {
        ptr=new char[max];
    }
    myqueue(const char *s):head(0),rear(0),num(0),max(10)
    {
        ptr=new char[max];
        for(int i=0;*(s+i)!=0;i++)
        {
            expend();
            *(ptr+i)=*(s+i);
            num++;
            rear++;
        }
    }
    ~myqueue(){}
    myqueue operator =(const myqueue &other)
    {
        if(this!= &other)
        {
            delete ptr;
            ptr=new char[other.max];
            this->num=other.num;
            for(int i=0;i<num;i++)
            {
                *(ptr+i)=*(other.ptr+i);
            }

        }
        return *this;
    }

    char front()
    {
        return *(ptr+head);
    }

    char back()
    {
        return *(ptr+rear);
    }

    bool empty()
    {
        return num==0;
    }

    int size()
    {
        return num;
    }

    void expend()
    {
        //如果为满,则扩容
        if(rear==max)
        {
            char *data;
            max=max*2;
            data=new char[max];
            memcpy(data,ptr,max/2);
            delete ptr;
            ptr=data;
        }

        return;
    }
    void push(char n)
    {
        expend();
        *(ptr+rear)=n;
        num++;
    }

    char pop()
    {
        num--;
        return *(ptr+head++);
    }
};


int main()
{
   myqueue q1("12345");

    cout<<q1.size()<<endl;

    return 0;
}

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

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

相关文章

GraphRAG工程落地成本详细解读和实例分析

最近半年GraphRAG引起不少关注&#xff0c;。GraphRAG代表了一种创新的方法&#xff0c;用于支持检索增强生成&#xff08;RAG&#xff09;应用&#xff0c;使组织能够从其复杂的数据集中提取前所未有的价值。然而&#xff0c;与大多数组织使用的相对简单的数据嵌入和向量化过程…

mkv怎么转换成mp4?2个简单易用的格式转换方法

小王立志成为一名vlogger&#xff0c;为此&#xff0c;他在旅行的时候拍摄了一段旅游视频&#xff0c;把视频保存在mkv格式中。在平台上传时小王才发现mkv视频不被平台支持。 小王傻眼了&#xff0c;视频需要把mkv转换成mp4格式才能发布&#xff0c;但mkv怎么转换成mp4&#x…

Socket编程---TCP篇

目录 一. TCP协议 二. 服务端模块代码实现 三. 服务端调用模块代码实现 四. 客户端模块代码实现 五. 初始版本结果展示 六. 多进程版服务端 七. 多线程版服务端 八. 线程池版服务端 前文已经讲了UDP的知识&#xff08;点此查看&#xff09;。今天来讲讲…

探索AWS EC2:云计算的强大引擎

在数字化转型的浪潮中&#xff0c;企业对计算资源的需求不断增长。亚马逊弹性计算云&#xff08;EC2&#xff09;作为AWS&#xff08;亚马逊网络服务&#xff09;的核心产品之一&#xff0c;凭借其强大的功能和灵活性&#xff0c;成为了全球企业构建和扩展应用的首选平台。无论…

K8S - 外部访问集群

前言 文档可以承接前面的内容看 这里只做外部访问的介绍 正文 kubectl get po#打印某个pod的环境变量 kubectl exec nginx-7c5ddbdf54-6nfw2 \-- printenv |grep KUBERNETES#删除 service kubectl get svc kubectl delete svc nginx#使用 LoadBalancer 的方式重新创建 servi…

Etherpad在线文档协作编辑工具

Etherpad在线文档协作编辑工具 一、前言 Etherpad是一种开源的实时协作编辑器&#xff0c;允许多个用户同时编辑同一文档&#xff0c;并实时显示每个用户的输入内容。Etherpad最初由Etherpad基金会开发&#xff0c;后来被Google收购&#xff0c;现在由Apache软件基金会维护。E…

ElasticSearch-集群架构

核心概念 节点类型分片集群搭建 ES安全认证 集群内部安全通信 生产环境常见集群部署方式 单一角色增加节点水平扩展读写分离架构异地多活架构Hot & Warm 架构集群容量规划 产品信息库搜索时间序列的数据 核心概念 ES集群架构的优势 提高系统的可用性&#xff0c;部分节点…

【QT】学习笔记:枚举桌面窗口句柄

在 Qt 中&#xff0c;虽然 Qt 本身没有直接提供枚举桌面窗口的 API&#xff0c;但可以通过调用 Windows API 来实现枚举桌面上所有窗口的句柄&#xff0c;包括子窗口以及子窗口与父窗口的关系。我们可以使用 Windows 的 EnumWindows 和 EnumChildWindows 函数来枚举所有顶层窗口…

C语言指针进阶三:(回调函数,qsort函数的模拟)

回调函数 回调函数就是通过函数指针调用的函数&#xff0c;如果你把函数的指针作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其指向的函数时&#xff0c;我们所说这就是回调函数。 qsort函数的使用&#xff08;回调函数案例&#xff09; 我们先看看qsort函数的…

【单调栈 】2289. 使数组按非递减顺序排列

本文涉及的基础知识点 单调栈分类、封装和总结 LeetCode2289. 使数组按非递减顺序排列 给你一个下标从 0 开始的整数数组 nums 。在一步操作中&#xff0c;移除所有满足 nums[i - 1] > nums[i] 的 nums[i] &#xff0c;其中 0 < i < nums.length 。 重复执行步骤&a…

【重磅推荐】《一本书读懂大模型:技术创新、商业应用与产业变革》发布!大模型零基础入门到精通

近日&#xff0c;由中国电信研究院天翼智库大模型研究团队编写、中国电信集团科技委主任邵广禄倾情作序的**《一本书读懂大模型&#xff1a;技术创新、商业应用与产业变革》**正式出版。本书系统介绍了大模型技术的发展历程、核心技术、行业应用、产业体系、治理问题以及未来展…

DeFi 发展的岔路口,Pencils Protocol带领投资者们“向前看”

DeFi 市场是否还存在 Alpha 机会&#xff1f; 走下坡路的 DeFi 去中心化金融&#xff08;DeFi&#xff09;曾是区块链世界发展的起点&#xff0c;也是链上世界流动性的重要支柱。然而&#xff0c;自 2021 年 DeFi 领域的总锁仓量&#xff08;TVL&#xff09;达到历史巅峰——…

模型压缩之剪枝

&#xff08;1&#xff09;通道选择 这里要先解释一下&#xff1a; &#xff08;1&#xff09;通道剪枝 那我们实际做法不是上面直接对所有层都添加L1正则项&#xff0c;而是仅仅对BN层权重添加L1正则项。通道剪枝具体步骤如下&#xff1a; 1.BN层权重添加L1正则项&#xf…

ElementUI实现el-table组件的合并行功能

前言 有时遇到一些需求&#xff0c;需要实现ElementUI中&#xff0c;el-tabled组件合并单元格的功能&#xff0c;稍微了解一下它的数据格式&#xff0c;不难可以写出比合并方法。但是在鼠标经过单元行时&#xff0c;会出现高亮的行与鼠标经过的行不一致的BUG。因此还需要实现c…

超级右键 - 为 Mac 的右键菜单升级一下

是不是有很多小伙伴&#xff0c;希望 Mac 也能像 Windows 一样&#xff0c;拥有丰富的右键菜单&#xff0c;快速完成新建、剪切、发送文件等操作。 一个叫作超级右键的工具就能做到&#xff0c;它能为 Mac 右键菜单增添多个功能选项&#xff0c;如 Win 系统般一键新建 / 剪切文…

vue通过html2canvas+jspdf生成PDF问题全解(水印,分页,截断,多页,黑屏,空白,附源码)

前端导出PDF的方法不多&#xff0c;常见的就是利用canvas画布渲染&#xff0c;再结合jspdf导出PDF文件&#xff0c;代码也不复杂&#xff0c;网上的代码基本都可以拿来即用。 如果不是特别追求完美的情况下&#xff0c;或者导出PDF内容单页的话&#xff0c;那么基本上也就满足业…

我的大模型岗位面试总结!太卷了!!!—我面试了24家大模型岗位 只拿了9个offer!

这段时间面试了很多家&#xff08;共24家&#xff0c;9个offer&#xff0c;简历拒了4家&#xff0c;剩下是面试后拒的&#xff09;&#xff0c;也学到了超级多东西。 大模型这方向真的卷&#xff0c;面试时好多新模型&#xff0c;新paper疯狂出&#xff0c;东西出的比我读的快…

传统CV算法——基于opencv的答题卡识别判卷系统

基于OpenCV的答题卡识别系统&#xff0c;其主要功能是自动读取并评分答题卡上的选择题答案。系统通过图像处理和计算机视觉技术&#xff0c;自动化地完成了从读取图像到输出成绩的整个流程。下面是该系统的主要步骤和实现细节的概述&#xff1a; 1. 导入必要的库 系统首先导入…

误删的PPT怎么恢复回来?

在日常工作和学习中&#xff0c;PPT已成为我们不可或缺的工具。然而&#xff0c;有时不小心误删重要的PPT文件&#xff0c;可能会让人倍感焦虑。别担心&#xff0c;本文将为你提供几种实用的方法&#xff0c;帮助你轻松恢复误删的PPT文件。 一、从回收站恢复 当你误删文件时&…

【Grafana】Prometheus结合Grafana打造智能监控可视化平台

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…