【第四节】C++的派生与继承

news2024/11/14 14:20:06

目录

一、继承特性

二、派生类的定义格式

三、派生类的继承方式

3.1 三种继承方式概述

3.2 接口继承和实现继承

四、派生类的构造和析构函数

五、类的成员重定义

六、多重继承

6.1 多继承

6.2 多继承中的二义性问题

七、虚基类

八、总结


一、继承特性

        在生物学中,遗传(或称继承)是指所有生物从其祖先那里继承特定特征的过程。这一概念同样适用于设计复杂系统,它提供了一种将系统组件组织成继承结构的方法,以便于系统描述,并且是实现代码重用的强大工具。

        在C++中,代码重用是一个核心特性。与C语言通过复制和修改代码来重用代码的低级方式不同,C++提供了继承和组合两种高级方式来实现代码重用。通过继承或组合现有类来创建新类,而不是从头开始编写。通常,这些类已经过充分测试,因此在继承时,潜在的错误仅可能出现在新增的代码部分。

        组合重用涉及将一个类作为另一个类的对象成员,通过委托成员对象来实现功能。另一种方式是继承。

继承的优势包括:

  1. 继承允许使用已存在的类来创建新类,新类继承了原有类的所有属性和操作,并可以在其基础上进行修改和扩展。

  2. 新类被称为派生类或子类,而原有类被称为基类或父类。

  3. 派生类是基类的具体化,通常情况下,派生类的表示范围比基类要小得多。

比如教师是一个人,人不一定是一个教师,派生类范围小且具体,基类范围大更抽象。

C++的继承支持单继承和多继承
单继承:派生类只有一个直接基类的继承方式
多继承:派生类有多个直接基类的继承方式

二、派生类的定义格式

单继承的基本格式如下:
class<派生类名>:<继承方式><基类名>
{
//<派生类新成员的定义>
}

其中,派生类名就是派生类的名字,并且派生类是按指定的继承方式派生的。继承方式有:
A. public  公有继承
B.private  私有继承
C.protected  保护继承

代码示例:

class CClassA{
public:
    CClassA(int nNum) {
        m_nNumA = nNum;
    }
    void print() {
        cout << m_nNumA << endl;
    }
private:
    int m_nNumA;
    };

class CClassB :public CClassA {
public:
    void print() {
        cout << m_nNumB << endl;
    }
private:
    int m_nNumB;
};

多继承的定义方式和单继承差不多。
class<派生类名>:<继承方式1)<基类名1〉,<继承方式2><基类名2>,……
{
//<派生类新成员定义>
};
多继承派生类有多个基类,基类名之间用逗号分隔,每个基类名前都应有一个该基类的继承方式说明,假如不写继承方式的话,默认的继承方式为私有继承。

三、派生类的继承方式

3.1 三种继承方式概述

        类的继承方式主要包括三种:

  1. 公有继承(public):在这种方式下,基类的每个成员在派生类中保持其原有的访问权限不变。

  2. 私有继承(private):选择私有继承时,基类的所有成员在派生类中均变为私有成员,且这些成员无法被派生类的子类进一步访问。

  3. 保护继承(protected):在保护继承模式下,基类中的公有成员和保护成员在派生类中转变为保护成员,而私有成员则保持其私有性质。

        无论采用哪种继承方式,派生类的成员函数及友元函数均有权访问基类中的公有成员和保护成员,但无法触及私有成员。特别地,在公有继承情况下,派生类的对象仅能访问基类的公有成员;而在保护继承和私有继承情况下,派生类的对象则无法访问基类中的任何成员。

3.2 接口继承和实现继承

        在面向对象编程中,我们通常将类的公有成员函数视为其接口,这些接口对外界提供服务和功能。

  • 公有继承(public inheritance):在这种继承模式下,基类的公有成员函数在派生类中保持其公有性质,即基类的接口被完整地继承并成为派生类的接口。因此,这种继承方式被称为接口继承。通过公有继承,父类的接口得以传递至子类,并且子类还可以继续向下派生至孙子类。

  • 实现继承:对于私有继承(private inheritance)和保护继承(protected inheritance),派生类并不继承基类的公有接口。这意味着派生类不再对外提供基类的公有接口,其目的仅在于重用基类的实现细节。因此,这两种继承方式被称为实现继承

    • 保护继承:保护继承导致基类的公有接口在派生类中失效,这些接口只能在派生类内部被调用。尽管如此,保护继承仍然允许派生类继续向下派生至孙子类。

    • 私有继承:私有继承同样使得基类的所有接口仅能在派生类内部使用。然而,与保护继承不同的是,私有继承切断了基类与后续孙子类之间的直接联系,即私有继承的派生类无法再向其子类传递基类的接口。

四、派生类的构造和析构函数

        基类的构造函数不被继承,派生类中需要声明自己的构造函数。声明构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化要调用基类构造函数完成。假如基类构造函数没有缺省的构造函数,派生类的构造函数需要给基类的构造函数传递参数。
<派生类名>(<总参数表>)
:<基类构造函数名>(<参数表 1>),<子对象名>(<参数表 2>)
{
//<派生类中数据成员的初始化>
}

请看如下示例代码:

class CBase {
public:
    CBase(int b) :m_b(b){
        cout << "CBase ..." << endl;
    }
    ~CBase() {
        cout << "~CBasecoutend" << endl;
    }
    int m_b;
};

class Derived : public CBase {
public:
    Derived(int b, int d) :m_d(d), CBase(b) {//调用父类构造函数
        cout << "Derived ..." <<endl;
    }
    ~Derived(){
        cout << "~Derived ..."<< endl;
    }
    int m_d;
};

        在C++中,当涉及到类的继承和包含对象成员时,构造函数和析构函数的调用顺序遵循特定的规则:

  • 构造函数的调用顺序

    1. 首先,调用基类的构造函数。如果基类提供了无参构造函数,则默认情况下会自动调用该构造函数;若基类没有提供无参构造函数,则派生类构造函数必须显式调用基类的某个构造函数。

    2. 其次,调用数据成员(即对象成员)的构造函数。

    3. 最后,调用派生类自身的构造函数。

  • 析构函数的调用顺序

    1. 首先,调用派生类的析构函数。

    2. 接着,调用数据成员的析构函数。

    3. 最后,调用基类的析构函数。

        值得注意的是,析构函数的调用顺序与构造函数相反,确保了在对象生命周期结束时,按照创建时的逆序进行资源清理和状态还原。

示例代码:

#include <iostream>
using namespace std;
class ObjectB {
public:
    ObjectB(int objb) :m_obj(objb) {
        cout << "ObjectB ..." << endl; 
    }
    ~ObjectB() {
        cout << "~0bjectB ..." << endl;
    }
    int m_obj;
};

class ObjectD {
public:
    ObjectD(int objd) :m_objd(objd) {
        cout << "ObjectD ..." << endl;
    }
    ~ObjectD() {
        cout << "~0bjectD ..." << endl;
    }
    int m_objd;
};

class Base{
public:
    Base(int b) :m_b(b), m_objb(111) {//对象成员没有默认构造函数,必须在初始化列表中显示调用构造函数。
        cout << "Base ..." << endl;
    }
    Base(const Base& other) :m_objb(other.m_objb), m_b(other.m_b) {
        cout << "Copy " << "Base ..." << endl;
    }
    ~Base(){
        cout << "~Base ..." << endl;
    }
    int m_b; ObjectB m_objb;
};

class Derived :public Base {
public:
    Derived(int b, int d) :m_d(d), Base(b), m_objd(222) {
        //基类,与对象成员没有默认构造函数,必须在初始化列表中显示调用构造函数
        cout << "Derived .."<<endl;
    }
    Derived(const Derived& other): m_d(other.m_d), m_objd(other.m_objd), Base(other) {
        cout << "Copy" << "Derived ..." << endl;
    }
    ~Derived() {
        cout << "~Derived .."<< endl;
    }
    int m_d; ObjectD m_objd;
};

int main() {
    Derived d(100, 200);
    cout << d.m_b << " " << d.m_d << endl; 
    return 0;
}

五、类的成员重定义

        在面向对象编程中,当子类需要对基类的某个成员进行功能上的修改或扩展时,会采用重定义(overwrite)机制。重定义主要分为以下两种情况:

  1. 对基类数据成员的重定义:子类可以重新定义与基类同名的数据成员,此时,派生类对象在访问该数据成员时,将直接引用派生类中的定义,从而隐式地屏蔽了基类中的同名数据成员。

  2. 对基类成员函数的重定义

    • 完全相同的重定义:子类重新定义一个与基类完全相同的成员函数(即函数名、参数列表和返回类型均一致),这种情况下,派生类对象调用该函数时,将执行派生类中的版本,基类的同名函数被隐藏。

    • 参数不同的重定义:子类定义一个与基类成员函数名相同但参数列表不同的函数,尽管参数不同,但这种做法仍属于重定义而非重载。同样地,基类的同名函数会被隐藏,调用时将执行派生类中的函数。

注意事项

  • 当基类与派生类中存在重定义的数据成员时,派生类对象访问的是派生类中的数据成员,基类的数据成员被隐藏。

  • 对于完全相同的重定义成员函数,基类的函数在派生类中被隐藏,调用时执行的是派生类的函数。

  • 即使成员函数名相同但参数不同,这种重定义也会隐藏基类的函数,这是一个需要特别注意的点。

  • 在不同作用域(如基类和派生类)中定义的函数,即使函数名相同且参数不同,也不构成重载,而是重定义。重载是指在同一作用域内,函数名相同但参数列表不同的现象,通常发生在同一个类内部。

示例代码:

#include <iostream>
using namespace std;

class Base {
public:
    Base() :m_x(0) {}

    int GetBaseX() const {
        return m_x;
    }
    void Show() {
        cout << "Base::Show ..."<< endl;
    }

    int m_x;
};


class Derived : public Base {
public:
    Derived() :m_x(0) {}

    int GetDerivedX() const {
        return m_x;
    }

    void Show(int n) {
        cout << "Derived::Show " << n << endl;
    }
    void Show() {
        cout << "Derived::Show .." << endl;
    }
    int m_x;
};


class Test {
public:
    Base m_b;
    int m_x;
};


int main() {
    Derived d;
    d.m_x = 10; //如果没有重定义的话,它改变的就是基类的
    d.Base::m_x = 20; //如果想使用基类的重定义的数据成员要是用作用域符号。
    cout<< d.GetBaseX()<< endl;
    
    cout << d.GetDerivedX() << endl; 
    d.Show();
    
    d.Base::Show();//想要调用父类的函数可以通过作用域符号
    cout << sizeof(Derived) << endl;
    cout << sizeof(Test) << endl;
    return 0;
}

六、多重继承

6.1 多继承

        有一些事物,在分类学上属于不同的分类,比如野鸭,是鸟,也是鸭。这个时候就需要用到多继承。
        单重继承--一个派生类最多只能有一个基类
        多重继承--一个派生类可以有多个基类,如果派生类有两个或两个以上的直接基类,称为多继承。
如下图所示:


多继承派生类的定义格式:
class<派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,...
{
    //派生类的类体
}

class CClassC :  public CClassB, private CClassA
{

}

多继承派生类的构造函数格式:
<派生类名>(<总参数表>)
:<基类名1>(<参数表1>),<基类名 2>(<参数表 2>),<子对象名 3>(<参数 3>),…
{
//派生类构造函数
}

CClassC(int nNum)
:CClassB(nNum),CClassA(nNum),mnNum(15)
{
}

        执行顺序:先执行所有基类的构造函数,再执行派生类本身的构造函数,包含子对象在内,请看下面的例子:
class A
{ ... };
class B
{ ... };
classC
{ ... };
class D:public A, public B, public C
{ ... };

        派生类D继承了三个基类,继承方式都是公有继承。类D的成员包含了类A、类B和类C中的成员,以及它本身的成员。如果要创建类D的对象,首先要顺序执行类A、类B和类C的构造函数,再执行派生类D本身的构造函数。

6.2 多继承中的二义性问题

        一般地,派生类成员的访问是唯一的。但是在多继承的情况下,可能出现派生类对其类成员访问的不唯一性,即二义性。下面是出现二义性的两种情况:调用不同类的具有相同名字成员时可能出现二义性:我们以下面这个沙发床例子来说明二义性问题。

示例代码:

#include <iostream>
using namespace std;

class Bed {
public:
    Bed(int weight) :m_weight(weight) {}
    void Sleep() {
        cout << "Sleep ..." << endl;
    }
    int m_weight;
};

class Sofa {
public:
    Sofa(int weight) :m_weight(weight) {}
    void watchTV() {
        cout << "Watch TV ..." << endl;
    }
    int m_weight;
};

class SofaBed :public Bed, public Sofa {//继承了之后,沙发床既可以睡觉,也可以看电视。
public:
    SofaBed() :Bed(0), Sofa(0) {
        FoldIn();
    }
    void FoldOut() {
        cout << "FoldOut ..." << endl;
    }

    void FoldIn() {
        cout << "FoldIn ..." << endl;
    }
};

int main() {
    SofaBed sofaBed;
    //sofaBed.mweight=10;//这样访问是不明确的
    //sofaBed.mweight=20://同上
    sofaBed.Bed::m_weight = 10;
    sofaBed.Sofa::m_weight = 20;
    sofaBed.watchTV();
    sofaBed.FoldOut();
    sofaBed.Sleep();
    return 0;
}

        但是我们思考:沙发床应该只有一个重量,不应该分成两个重量。很自然的我们可以想到:将沙发和床进行抽象为一个整体,比如家具,在家具中安插一个重量,代码如下:

#include <iostream>
using namespace std;

class Furniture {
public:
    Furniture(int weight) :m_weight(weight) {}
    int m_weight;
};

class Bed : public Furniture {
public:
    Bed(int weight) : Furniture(weight) {}
    void Sleep() {
        cout << "Sleep ..." << endl;
    }
    int m_weight;
};

class Sofa : public Furniture {
public:
    Sofa(int weight) :Furniture(weight) {}
    void watchTV() {
        cout << "Watch TV ..." << endl;
    }
    int m_weight;
};

class SofaBed :public Bed, public Sofa {
public:
    SofaBed() :Bed(0), Sofa(0) {
        FoldIn();
    }
    void FoldOut() {
        cout << "FoldOut ..." << endl;
    }

    void FoldIn() {
        cout << "FoldIn ..." << endl;
    }
};

int main() {
    SofaBed sofaBed;
    //sofaBed.m_weight=10;//这样访问是不明确的
    //sofaBed.m_weight=20://同上
    sofaBed.Bed::m_weight = 10;
    sofaBed.Sofa::m_weight = 20;
    sofaBed.watchTV();
    sofaBed.FoldOut();
    sofaBed.Sleep();
    return 0;
}

        这么安排,可能很多人就会觉得问题已经解决了,但是这里依然还是会出现二义性问题因为从 C++继承的角度讲,Bed中包含一个furniture,sofa中包含一个furnitur。所以重量这个东西还是有两份。

那该怎么办呢?这下就要用虚继承来解决这个问题。

七、虚基类

        在多重继承的架构中,当一个派生类继承自多个基类,而这些基类又共享同一个共同的基类时,访问该共同基类中的成员可能会引发二义性问题。为了解决这一问题,C++引入了虚基类的概念。

        虚基类的引入旨在应对存在共同基类的情况,其主要作用是消除因多次继承同一基类而可能产生的二义性。通过使用虚基类,可以确保在最底层的派生类中,对于共同的基类成员只有一份唯一的副本,避免了数据的重复。

        具体而言,如果一个派生类拥有多个直接基类,而这些直接基类均继承自同一个共同的基类,那么在最终的派生类中,原本会保留该间接共同基类的多份同名数据成员。C++通过提供虚基类的机制,确保在继承过程中仅保留一份共同基类的成员,从而避免了成员的重复和潜在的二义性问题。

虚基类(virtual base class)说明格式如下:
virtual<继承方式><基类名>
virtual public CClassA

代码示例:

#include <iostream>
using namespace std;

class Furniture {
public:
    Furniture(int weight) :m_weight(weight) {}
    int m_weight;
};

class Bed : virtual public Furniture {
public:
    Bed(int weight) : Furniture(weight) {}
    void Sleep() {
        cout << "Sleep ..." << endl;
    }
};

class Sofa : virtual public Furniture {
public:
    Sofa(int weight) :Furniture(weight) {}
    void watchTV() {
        cout << "Watch TV ..." << endl;
    }
};

class SofaBed :public Bed, public Sofa {
public:
    SofaBed() :Bed(0), Sofa(0) {
        FoldIn();
    }
    void FoldOut() {
        cout << "FoldOut ..." << endl;
    }

    void FoldIn() {
        cout << "FoldIn ..." << endl;
    }
};

int main() {
    SofaBed sofaBed;
    sofaBed.m_weight = 10;
    return 0;
}

使用虚继承后,可以实现如下的效果,项层类中的数据在对象中只会存在一份。

八、总结

        C++语言提供了类继承的机制,这是面向对象编程中的核心概念之一。在继承关系中,派生类的成员函数和友元函数能够访问基类中所有的公有和保护类型的数据成员及成员函数。然而,派生类对象本身仅能访问基类中的公有数据成员和成员函数。

        多重继承是指一个类从多个基类中继承特性的机制,派生类因此获得了所有基类的属性。当一个类作为两个或更多基类的派生类时,必须在派生类的声明中,在类名和冒号后面列出所有基类的名称,各基类之间以逗号分隔。派生类的构造函数有责任激活所有基类的构造函数,并将必要的参数传递给它们。

        派生类本身也可以成为其他类的基类,从而形成一个继承链。在这种情况下,当派生类的构造函数被调用时,它的所有基类的构造函数也会被依次激活。为了解决多重继承中可能出现的二义性问题,C++引入了虚基类的概念。虚基类的目的是确保公共基类在其派生对象中仅产生一个基类子对象,从而避免因多次继承同一基类而导致的重复和冲突。

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

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

相关文章

命令行解析器浅解

1、什么叫解析器&#xff1f; 解析器&#xff08;parser&#xff09;是一种程序或组件&#xff0c;用于分析输入的数据&#xff0c;并将其转换为更易于处理的格式。解析器在计算机科学中有广泛的应用&#xff0c;特别是在编译器、解释器、自然语言处理和数据格式转换等领域。 1…

2019美亚

1.何源是一名 25 岁的客服人员&#xff0c;在一间电讯公司工作。某日&#xff0c;何源在用 iPhone 手机在政府建筑物 中偷拍车牌期间被警员截停&#xff0c;盘问期间警员检查手机相册发现多张车牌图片&#xff0c;何源情绪紧张&#xff0c;趁 警员不被&#xff0c;抢过手机丢入…

vmware中Ubuntu虚拟机和本地电脑Win10互相ping通

初始状态 使用vmware17版本安装的Ubuntu的20版本&#xff0c;安装之后什么配置都要不懂&#xff0c;然后进行下述配置。 初始的时候是NAT&#xff0c;没动的. 设置 点击右键编辑“属性” 常规选择“启用”&#xff1a; 高级选择全部&#xff1a; 打开网络配置&#xff0c;右键属…

Linux学习笔记(epoll,IO多路复用)

Linux learning note 1、epoll的使用场景2、epoll的使用方法和内部原理2.1、创建epoll2.2、使用epoll监听和处理事件 3、示例 1、epoll的使用场景 epoll的英文全称是extend poll&#xff0c;顾名思义是poll的升级版。常见的IO复用技术有select&#xff0c;poll&#xff0c;epo…

方法的重写--5.29

当子类对父类的方法不满意时&#xff0c;可以进行重写&#xff0c;但是方法名字要与父类一样。 举例&#xff0c;我用people来举例&#xff0c;我是打工人&#xff0c;然后再创一个student类&#xff0c;重写方法我不是打工人&#xff0c;我是读书人。代码如下&#xff0c;发现…

MindSpore实践图神经网络之环境篇

MindSpore在Windows11系统下的环境配置。 MindSpore环境配置大概分为三步&#xff1a;&#xff08;1&#xff09;安装Python环境&#xff0c;&#xff08;2&#xff09;安装MindSpore&#xff0c;&#xff08;3&#xff09;验证是否成功 如果是GPU环境还需安装CUDA等环境&…

【busybox记录】【shell指令】mkfifo

目录 内容来源&#xff1a; 【GUN】【mkfifo】指令介绍 【busybox】【mkfifo】指令介绍 【linux】【mkfifo】指令介绍 使用示例&#xff1a; 创建管道文件 - 创建的时候同时指定文件权限 常用组合指令&#xff1a; 指令不常用/组合用法还需继续挖掘&#xff1a; 内容来…

ABAP MD04增强排除MRP元素

场景 MD04跑出来很多MRP元素&#xff0c;用户想手工控制某些MRP元素不参与运算 分析 增强点还蛮好找的&#xff0c;控制MRP元素是否参与运算用下面的se19三代增强点就可以&#xff0c;打个断点看下MD04进的哪个增强点就行 旧版本的用这个&#xff1a;MD_CHANGE_MRP_DATA 新…

鸿蒙开发【实现页面路由跳转】接上一个微博页面

给顶部最左边的日历图标设置点击事件实现页面跳转 需要展示页面内容示例图&#xff1a; 6.1.1.设置页面头部内容 新建一个页面命名为MydailyPage &#xff0c;给整个页面设置背景属性 代码如下&#xff1a; Entry Componentstruct MydailyPage { build() { Column() { …

linux Inodes满导致数据库宕机

项目经理反馈集群环境中有个节点无法使用了需要支援下&#xff0c;同时发过来截图说明磁盘还是有空的。 登录系统后直接发现问题 orcl2:/home/oracledb2> sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 Production on Wed May 29 13:59:21 2024 Copyright (c) 1982,…

云原生架构内涵_3.主要架构模式

云原生架构有非常多的架构模式&#xff0c;这里列举一些对应用收益更大的主要架构模式&#xff0c;如服务化架构模式、Mesh化架构模式、Serverless模式、存储计算分离模式、分布式事务模式、可观测架构、事件驱动架构等。 1.服务化架构模式 服务化架构是云时代构建云原生应用的…

串口通信问题排查总结

串口通信问题排查 排查原则&#xff1a; 软件从发送处理到接收处理&#xff0c;核查驱动、控制及发送接收数据是否正常。硬件从发送到接收&#xff0c;针对信号经过的各段&#xff0c;分段核对信号是否正常。示波器、逻辑分析仪。用万用表、示波器、逻辑分析仪等工具&#xf…

小程序如何更换营业执照

​因为商家经营业务的变更&#xff0c;尤其是之前的营业执照注销等原因&#xff0c;导致要求更换小程序主体。下面就具体介绍如何进行变更。 1. 登录mp.weixin.qq.com&#xff0c;找到设置->基本设置&#xff0c;在主体信息字段&#xff0c;点击小程序主体变更。主体变更分…

MVC架构中的servlet层重定向404小坑

servlet层中的UserLoginServlet.java package com.mhys.servlet; /*** ClassName: ${NAME}* Description:** Author 数开_11* Create 2024-05-29 20:32* Version 1.0*/import com.mhys.pojo.User; import com.mhys.service.UserService; import com.mhys.service.impl.UserSer…

tomcat学习--部署java项目

主流开发项目&#xff0c;springboot框架下&#xff0c;jar部署java传统的tomcat发布war包 一 什么是tomcat&#xff1f; 是一个用于运行java程序的软件&#xff0c;发布的时候&#xff1a;开发将源码使用maven打包&#xff0c;生产war包 二 安装tomcat tomcat是java写的&a…

蓝桥杯-AB路线(详细原创)

问题描述&#xff1a; 有一个由 N M 个方格组成的迷宫&#xff0c;每个方格写有一个字母 A 或者 B。小蓝站在迷宫左上角的方格&#xff0c;目标是走到右下角的方格。他每一步可以移动到上下左右相邻的方格去。 由于特殊的原因&#xff0c;小蓝的路线必须先走 K 个 A 格子、再…

计算机系统基础实验三(解了但尽量理解)

一.准备阶段 1、下载好32位的实验代码后&#xff0c;将文件解压缩并且通过共享文件夹操作将文件添加到虚拟机中&#xff0c;双击查看bomb.c代码&#xff0c;将c代码完整看了一遍&#xff0c;发现看这里的c代码是无从下手的&#xff0c;代码中只含有主函数&#xff0c;触发炸弹…

19 - grace数据处理 - 补充 - 地下水储量计算过程分解 - 冰后回弹(GIA)改正

19 - grace数据处理 - 补充 - 地下水储量计算过程分解 - 冰后回弹(GIA)改正 0 引言1 gia数据处理过程0 引言 由水量平衡方程可以将地下水储量的计算过程分解为3个部分,第一部分计算陆地水储量变化、第二部分计算地表水储量变化、第三部分计算冰后回弹改正、第四部分计算地下…

【已解决】使用token登录机制,token获取不到,blog_list.html界面加载不出来

Bug产生 今天使用token完成用户登录信息的存储的时候被卡了大半天。 因为登录的功能写的已经很多了&#xff0c;所以今天就没有写一点验一点&#xff0c;而是在写完获取博客列表功功能&#xff0c;验证完它的后端后&#xff0c;了解完令牌的基本使用以及Jwt的基本使用方式——…

【高校科研前沿】南科大姜丽光课题组在地球物理学领域TOP期刊Geophys. Res. Lett.发表极端气候频发下水库蓄水状态的相关研究成果

文章简介 论文名称&#xff1a;Reservoir Filling Up Problems in a Changing Climate:Insights From CryoSat‐2 Altimetry 第一作者及单位&#xff1a;汪志伟&#xff08;硕士研究生 南方科技大学环境学院&#xff09; 通讯作者及单位&#xff1a;姜丽光&#xff08;助理教…