Linux 学习记录44(C++篇)

本文详细介绍了C++中的静态成员变量和函数,包括它们的定义、作用和使用方式。接着讨论了继承的概念、格式、成员的继承规则以及特殊成员函数(构造、析构、拷贝构造、拷贝赋值)在继承中的处理。最后,讲解了多态性,特别是虚函数在函数重写中的应用。文章还包含了多重继承、菱形继承及其解决方案——虚继承的讨论。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux 学习记录44(C++篇)

在这里插入图片描述

一、静态成员变量/函数

1. 静态成员变量

static修饰的成员变量被称为静态成员变量

  1. 不依赖于类对象的,独立于类体存在的,编译阶段分配在静态区
  2. 静态成员变量不占用类的大小
  3. 所有类对象共用同一个静态成员,如果通过某个类对象修改过静态成员变量,其他类对象的静态成员变量也会被修改
  4. 即使没有实例化类对象,也可以使用类中的静态成员变量
  5. 类在的静态成员变量一般是public权限的
  6. 静态成员变量需要在全局处声明,声明时可以给初始值也可以不给,不给就是0
class test
{
private:
    string str;
public:
    /*定义类中的静态成员变量*/
    static int num;
};

/*声明类中的静态成员变量*/
int test::num=10;

int main()
{
    return 0;
}

2. 静态成员函数

static修饰的成员函数被称为静态成员函数

  1. 独立于类外,不占用类的大小
  2. 调用:直接使用类名调用使用类对象调用
  3. 静态成员函数不能使用非静态成员变量
  4. 静态成员函数和非静态成员函数同名时不构成函数重载
  5. 静态成员函数和非静态成员函数作用域不同
  6. 静态成员函数没有this指针
class test
{
private:
    string str;
public:
    /*定义类中的静态成员变量*/
    static int num;
    static void show(void)
    {
        cout << num << endl;
    }
};

/*声明类中的静态成员变量*/
int test::num=10;

int main()
{
    test::show();
    test buf1;
    test buf2;
    buf1.num=20;
    buf2.show();
    return 0;
}

输出:
在这里插入图片描述

二、继承

继承指基于已有的类创建处新类的过程 【基类/派生类】 【父类/子类】

1. 继承的作用

1. 能提高代码的复用性,[父类/基类]中原有的内容在[子类/派生类]中无需再定义
2. 继承是实现多台的前提

2. 继承的格式

例如:已有class A,创建 class B继承自A
class B:权限A -----> 创建一个B类,用特点的权限继承方式继承自A
A类可以称为 父类、基类
B类可以成为子类、派生类
class B:public A{} //B类公有继承A类
class B:private A{} //B类私有继承A类
class B:protected A{} //B类受保护的继承A类
父类中的访问权限public:private:protectedpublic:private:protectedpublic:private:protected
继承方式publicprivateprotected
子类中的访问权限public:不能访问:protectedpublic:private:不能访问public:private:不能访问

3. 子类对父类中成员的继承

1. 子类汇继承父类中的所有成员,包括私有成员
2. 类之间的继承关系,可以理解为是【包含关系】
3. 子类中从父类继承的成员,先存放,放在首地址,父类的指针/引用可以指向子类的对象
4. 父类的指针,只能访问父类的空间,子类的指针可以访问的空间包含父类继承的和子类拓展的

4. 子类中存在和父类同名成员时

1. 通过子类对象默认访问的是,子类的成员
2. 如果想要访问父类的成员,使用类名加上域标识符可以访问父类中的内容
3. 也可以通过父类的指针指向子类对象,再来访问

5. 继承中特殊的成员函数

构造函数、析构函数、拷贝构造函数、拷贝赋值函数 都不会被继承

(1. 构造函数

1. 父类中的构造函数不会被继承
2. 实例化子类对象时,先调用父类中的构造函数,再调用子类中的构造函数
3. 如果父类中只有有参构造,子类必须在构造函数的初始化列表中显性的调用父类的有参构造

(2. 析构函数

  1. 父类中的析构函数不会被子类继承
  2. 先析构子类,再析构父类
  3. 有指针成员的情况:如果父类中有指针成员,需要在父类的析构函数中手动释放堆区的空间;如果子类中有指针成员,需要在子类的析构函数中手动释放堆区的空间
  4. 父类的析构函数,不需要再子类中手动调用,系统会自动调用析构函数

(3. 拷贝构造函数

申请空间并初始化

  1. 父类的拷贝构造不会被继承
  2. 如果父类中有指针成员,需要显性写出父类的深拷贝函数;如果指针成员在子类中九显性写出子类的深拷贝函数
  3. 子类的拷贝构造需要显性调用父类的拷贝构造,直接传子类的对象

(4. 拷贝赋值函数

拷贝赋值函数也涉及到深浅拷贝问题

  1. 拷贝赋值函数也不会被继承
  2. 子类和父类有不同的拷贝赋值函数
  3. 如果父类中有指针成员,就显性写出父类的深拷贝赋值函数,如果子类中有指针成员,就显性写出子类的深拷贝赋值函数,如果显性写出了子类的深拷贝赋值,子类的拷贝赋值函数内一定要显性调用父类的拷贝赋值函数。

(综合示例

#include <iostream>
using namespace std;

class Person
{
private:
    int *age;
    string name;
public:
    //无参构造
    Person():age(new int){cout << "Per无参构造" << endl;}
    //有参构造
    Person(string name,int age):name(name),age(new int(age))
    {cout << "Per的有参构造" << endl;}
    //拷贝构造
    Person(Person &other):age(new int(*(other.age)))
    {
        //*(this->age) = *(other.age);
        this->name = other.name;
    }
    ~Person()
    {
        cout << "释放了堆区的空间" << age << endl;
        delete age;
        cout << "Person的析构函数" << endl;
    }
    void show()
    {
        cout << "Per中age" << *age << endl;
    }
    void show_()
    {
        cout << age << endl;
    }
    //Person的深拷贝赋值
    Person &operator=(const Person &other)
    {
        if(this!=&other)
        {
            this->name = other.name;
            *(this->age) = *(other.age);
        }
        return *this;
    }
};
//定义Stu类继承自Person
//类默认是私有继承,常用的继承方式是公有的
class Stu:public Person
{
public:
    int score;
public:
    Stu(){}
    //子类的有参构造,在初始化列表中显性的调用父类的有参构造
    Stu(string name,int age,int score):Person(name,age),score(score)
    {cout << "Stu的有参构造" << endl;}
//    void show()
//    {
//        //cout << name << endl;
//        //cout << "Person的name" << Person::name << endl;
//        //子类中不能访问父类继承下来的私有成员
//        //cout << "Person中的name" << name << endl;
//        //子类中可以访问从父类继承下来的受保护的成员
//        //cout << "Person中的age" << age << endl;
//    }
    ~Stu(){cout << "Stu的析构" << endl;}
    //Person(Person &other)
    Stu(Stu &other):Person(other)   //对于Person的拷贝构造,传子类的对象,父类的引用可以引用子类的对象
    {
        this->score = other.score;
        cout << "Stu的拷贝构造" << endl;
    }
    Stu &operator=(const Stu &other)
    {
        if(this!=&other)
        {
            //显性调用Person的拷贝赋值
            Person::operator=(other);
            cout << "Stu的拷贝赋值函数" << endl;
            this->score = other.score;
        }
        return *this;
    }
};

int main()
{
    Stu s;   //无参构造
    s.show_();
    Stu s1("zhangsan",20,100);  //有参构造
    cout << "s1" << "\t" ;
    s1.show();
    //拷贝构造
    //Stu s2 = s1;  //调用拷贝构造,需要开辟空间
    s = s1;
    cout << "s" << "\t" ;
    s.show();
//    cout << "s1的地址" << &s1 << endl;  //a0
//    s1.Person::show();
//    Person *p = &s1;
//    p->show();  //通过父类指针访问到的是父类中的show函数
//    cout << "s1中首成员的地址" << &s1.score << endl;
    return 0;
}

6. 多重继承

一个子类由多个父类继承而来

(1. 格式

class B:public A,private C
class 类名:继承权限1 类1,继承权限2 类2......
{
	子类拓展的内容
}

例:

#include <iostream>
#include <iomanip>
#include <cstring>
#include <cstdio>
using namespace std;

class test1
{
private:
    string str;
    int* num;
public:
protected:
};
class test2
{
private:
    string str;
    int* num;
public:
protected:
};
class test3
{
private:
    string str;
    int* num;
public:
protected:
};

/*定义stu类以公有权限多重继承自test*/
class stu : public test1,public test2,public test3
{
private:
    int score;
public:
};

int main()
{
    stu buf1;
    return 0;
}

(2. 注意事项

1. 如果多个基类(父类)中有同名成员,会发生歧义,通过类名加上域限定符访问指定的成员
2. 特殊的成员函数调用和使用的规律和普通继承时一致
3. 多重继承时,构造函数的调用顺序,和继承的顺序有关,和初始化列表中的调用顺序无关

7. 菱形继承(钻石继承)

在这里插入图片描述

1. 公共基类的内容会在汇集子类中保留两份
2. 对于访问公共基类中成员时,会存在二义性的问题
3. 如果多次继承大型的公共基类,会导致子类的内存过大。

(1. 虚继承(virtual)

使用virtual关键字修饰的变量是虚继承

  1. 虚继承用于解决菱形继承存在的问题,通过菱形继承公共基类中的额内容只会在汇集子类中保存一份,不会造成子类的内存过大。
  2. 如果使用虚继承,对于公共基类的构造函数,需要直接使用基类名来调用(因为不知道通过那一条中间路径继承的基类)
  3. 虽然,只保留了一份公共基类,但是仍然可以通过指定路径来访问基类中的成员

例:

class Person //公共基类
{
public:
    string name;
    int age;
    Person(){cout << "p无参构造" << endl;}
    Person(string name,int age):name(name),age(age){}
};
//Stu虚继承Person
class Stu:virtual public Person
{
public:
    int score;
    Stu(){cout << "Stu无参构造" << endl;}
    Stu(int score):score(score){}
};
//B虚继承Person
class B:virtual public Person
{
    int bb;
public:
    B(){}
    B(int b):bb(b){}
};
class A:public B,public Stu//汇集子类
{
    int high;
public:
    A(){cout << "A无参构造" << endl;}
    A(int h,int s,int b,int a,string name):high(h),Stu(s),B(b),Person(name,a)
    {
    }
};
int main()
{
    //A a1;  //Person的构造函数先被调用
    A a1(100,78,9,18,"zhangsan");
    cout << "a2****************************" << endl;
//    cout << a1.B::age << endl;   //指定访问从B路径继承下来的age
//    cout << a1.Stu::age << endl;   //指定访问从Stu路径继承下来的age
    cout << a1.age << endl;   //直接通过汇集子类来访问基类中的成员
    return 0;
}

三、多态

静态多态:也称为编译期间的多态,编译器在编译期间完成的
动态多态:即运行时的多态,在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法

多态按字面的意思就是多种形态,相同的方法调用,但是有不同的实现方式。多态性可以简单地概括为“一个接口,多种方法,实现接口与实现的分离。

1. 函数重写(override)

函数重写发生在父子类之间,指的是,在子类中重写父类的函数

要求:
1. 函数名相同
2. 参数列表相同
3. 函数声明相同
4. 只有虚函数能进行重写

(1. 虚函数(virtual)

使用virtual修饰的函数就是虚函数

  1. 只要父类中某个函数被定义为了虚函数,后面所有继承自该类的该成员函数都是虚函数
  2. 虚函数可以在子类中对父类继承的虚函数进行重写,如果不重写使用的还是父类中的函数
  3. 体现出函数重写的条件(父类的指针指向子类的成员)

思维导图

在这里插入图片描述

练习

全局变量,int monster = 10000;定义英雄类hero,受保护的属性string name,int hp,int attck;公有的无参构造,有参构造,虚成员函数 void Atk(){blood-=0;},法师类继承自英雄类,私有属性 int ap_atk=50;重写虚成员函数void Atk(){blood-=(attck+ap_atk);};射手类继承自英雄类,私有属性 int ac_atk = 100;重写虚成员函数void Atk(){blood-=(attck+ac_atk);}实例化类对象,判断怪物何时被杀死。

#include <iostream>
#include <iomanip>
#include <cstring>
#include <cstdio>
using namespace std;

class hero
{
private:

public:
    /*无参构造*/
    hero(){}
    /*有参构造*/
    hero(string str,int num1,int num2):name(str),Hp(num1),attck(num2){}
    /**/
    virtual void Atk(int* blood)
    {
        *blood-=0;
    }
protected:
    string name;
    int Hp;
    int attck = 60;
};

class Master:public hero
{
private:
    int ap_atk=50;
public:
    /*无参构造*/
    Master(){}
    /*有参构造*/
    Master(int num1):ap_atk(num1){}
    /**/
    void Atk(int* blood)override
    {
        *blood-=(attck+ap_atk);
    }
protected:
};

class shooter:public hero
{
private:
    int ac_atk=100;
public:
    /*无参构造*/
    shooter(){}
    /*有参构造*/
    shooter(int num1):ac_atk(num1){}
    /**/
    void Atk(int* blood)override
    {
        *blood-=(attck+ac_atk);
    }
protected:
};

int monster = 10000;

int main()
{
    Master buf1;
    shooter buf2;

    int i = 0;
    for(i=0;monster>0;i++) {
        buf1.Atk(&monster);
        buf2.Atk(&monster);
    }
    cout << "经过" << i << "次" <<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值