精华内容
下载资源
问答
  • 2020-07-09 14:46:28

    通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。

    在C++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承;从多个基类派生的继承称为多继承。

    派生类的定义格式

    单继承的定义格式如下:

    class <派生类名>:<继承方式><基类名>
        {
         <派生类新定义成员>
        };
    

    其中,<派生类名>是新定义的一个类的名字,它是从<基类名>中派生的,并且按指定的<继承方式>派生的。<继承方式>常使用如下三种关键字给予表示:

    
    ```csharp
    public 表示公有基类;
    
       private 表示私有基类;
    
       protected 表示保护基类;
    
    
      多继承的定义格式如下:
    
     
    
      class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
        {
         <派生类新定义成员>
        };
    
      可见,多继承与单继承的区别从定义格式上看,主要是多继承的基类多于一个。
    
      派生类的三种继承方式
    
       公有继承(public)、私有继承(private)、保护继承(protected)是常用的三种继承方式。
    
      1. 公有继承(public)
    
      公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。
    
      2. 私有继承(private)
    
      私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。
    
      3. 保护继承(protected)
    
      保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。
    
      下面列出三种不同的继承方式的基类特性和派生类特性。
    
      不同继承方式的基类和派生类特性
    
    
    继承方式	基类特性	派生类特性
    
    ```c
    公有继承	public	public
    protected
    private	protected
    不可访问
    私有继承	public	private
    protected
    private	private
    不可访问
    保护继承	public	protected
    protected
    private	protected
    不可访问
    

    为了进一步理解三种不同的继承方式在其成员的可见性方面的区别,下面从三种不同角度进行讨论。

    对于公有继承方式:
     
      (1) 基类成员对其对象的可见性:

    公有成员可见,其他不可见。这里保护成员同于私有成员。

    (2) 基类成员对派生类的可见性:

    公有成员和保护成员可见,而私有成员不可见。这里保护成员同于公有成员。

    (3) 基类成员对派生类对象的可见性:

    公有成员可见,其他成员不可见。

    所以,在公有继承时,派生类的对象可以访问基类中的公有成员;派生类的成员函数可以访问基类中的公有成员和保护成员。这里,一定要区分清楚派生类的对象和派生类中的成员函数对基类的访问是不同的。

    对于私有继承方式:

    (1) 基类成员对其对象的可见性:

    公有成员可见,其他成员不可见。

    (2) 基类成员对派生类的可见性:

    公有成员和保护成员是可见的,而私有成员是不可见的。

    (3) 基类成员对派生类对象的可见性:

    所有成员都是不可见的。

    所以,在私有继承时,基类的成员只能由直接派生类访问,而无法再往下继承。

    对于保护继承方式:

    这种继承方式与私有继承方式的情况相同。两者的区别仅在于对派生类的成员而言,对基类成员有不同的可见性。

    上述所说的可见性也就是可访问性。关于可访问性还有另的一种说法。这种规则中,称派生类的对象对基类访问为水平访问,称派生类的派生类对基类的访问为垂直访问。

    一般规则如下:

    公有继承时,水平访问和垂直访问对基类中的公有成员不受限制;

    私有继承时,水平访问和垂直访问对基类中的公有成员也不能访问;

    保护继承时,对于垂直访问同于公有继承,对于水平访问同于私有继承。

    对于基类中的私有成员,只能被基类中的成员函数和友元函数所访问,不能被其他的函数访问。

    基类与派生类的关系

    任何一个类都可以派生出一个新类,派生类也可以再派生出新类,因此,基类和派生类是相对而言的。

    基类与派生类之间的关系可以有如下几种描述:

    1. 派生类是基类的具体化

    类的层次通常反映了客观世界中某种真实的模型。在这种情况下,不难看出:基类是对若干个派生类的抽象,而派生类是基类的具体化。基类抽取了它的派生类的公共特征,而派生类通过增加行为将抽象类变为某种有用的类型。

    2. 派生类是基类定义的延续

    先定义一个抽象基类,该基类中有些操作并未实现。然后定义非抽象的派生类,实现抽象基类中定义的操作。例如,虚函数就属此类情况。这时,派生类是抽象的基类的实现,即可看成是基类定义的延续。这也是派生类的一种常用方法。

    3. 派生类是基类的组合

    在多继承时,一个派生类有多于一个的基类,这时派生类将是所有基类行为的组合。

    派生类将其本身与基类区别开来的方法是添加数据成员和成员函数。因此,继承的机制将使得在创建新类时,只需说明新类与已有类的区别,从而大量原有的程序代码都可以复用,所以有人称类是“可复用的软件构件”。

    更多相关内容
  • 基类和派生类

    2021-08-12 09:20:45
    其中,= default 既可以声明一起出现在的内部,也可以作为定义出现在的外部。其他函数一样,如果= default 在的内部,则默认构造函数是内联的;如果它在的外部,则该成员默认情况下不是内联的。 之所以...
    Quote() = default;
    
    因为该构造函数不接受任何实参,所以它是一个默认构造函数。在C++11标准中,如果我们需要默认的行为,可以通过在参数列表后面写上 = default 来要求编译器生成构造函数。其中,= default 既可以和声明一起出现在类的内部,也可以作为定义出现在类的外部。和其他函数一样,如果= default 在类的内部,则默认构造函数是内联的;如果它在类的外部,则该成员默认情况下不是内联的。

    之所以有效,是因为对内置类型的数据成员提供了初始值。



    定义基类

    class Quote1 {
    public:
    	Quote1() = default;
    	Quote1(const string& book, double sales_price) :bookNo(book), price(sales_price) {}
    	string isbn()const { return bookNo; }
    	// 返回给定数量的书籍销售总额
    	// 派生类负责改写并使用不同的则扣计算算法
    	virtual double net_price(size_t n)const {
    		return n * price;
    	}
    	virtual ~Quote1() = default;// 对析构函数进行动态绑定
    private:
    	string bookNo;				// 书籍的 ISBN 编号
    protected:
    	double price = 0.0;			// 普通状态下不打折的价格
    };
    

    基类通常都应该定义一个虚构函数,即使该函数不执行任何实际操作也是如此。

    成员函数与继承

    派生类可以继承其基类的成员,然而遇到与类型相关的操作时,派生类必须对其重新定义。即,派生类需要对这些操作提供自己的新定义以覆盖(voerride)从基类继承而来的旧定义。
      在C++语言中,基类必须将它的两种成员函数分开来:一种是基类希望其派生类进行覆盖的函数;另一种是基类希望派生类直接继承而不要改变的函数。对于前者,基类通常将其定义为虚函数(virtual)。当使用指针或引用调用虚函数时,该调用将被动态绑定。
      任何构造函数之外的非静态函数都可以是虚函数。关键字virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义。
      成员函数如果没被声明为虚函数,则其解析过程发生在编译时而非运行时。对于isbn成员来说这正是我们希望看到的结果。isbn函数的执行与派生类的细节无关,不管作用于Quote对象还是Bulk_quote对象,isbn函数的行为都一样。在继承层次关系中只有一个isbn函数,因此也就不存在调用isbn()时到底执行哪个版本的疑问。

    访问控制与继承

    派生类可以继承定义在基类中的成员,但是派生类的成员函数不一定有权访问从基类继承而来的成员。和其他使用基类的代码一样,派生类能访问共有成员,而不能访问私有成员。不过在某些时候基类中还有这样一类成员,基类希望它的派生类有权访问该成员,同时禁止其他用户访问。用受保护的(protected)访问运算符说明这样的成员。
      Quote类希望它的派生类定义各自的net_price函数,因此派生类需要访问Quote的price成员。此时我们将price定义成受保护的。

    定义派生类

    派生类必须通过使用类派生列表(class derivation list)明确指出它是从哪些基类继承来的。
      派生类必须将其继承而来的成员函数中需要覆盖的那些重新声明。

    class Bulk_quote1 :public Quote1 {
    public:
    	Bulk_quote1() = default;
    	Bulk_quote1(const string&, double, size_t, double);
    	// 覆盖基类的函数版本以实现基于大量购买的则扣政策
    	double net_price(size_t)const override;
    private:
    	size_t min_qty = 0;			// 适用折扣政策的最低购买量
    	double discount = 0.0;		// 以小数表示的折扣额
    };
    

    如果一个派生是公有的,则基类的公有成员也是派生类接口的组成部分。此外,能将公有派生类型的对象绑定到基类的引用或指针上。
      因为在派生列表中使用了public,所以Bulk_quote的接口隐式地包含isbn函数,同时在任何需要Quote的引用或指针的地方我们都能使用Bulk_quote的对象。

    派生类中的虚函数

    派生类不总是覆盖它继承的虚函数。如果派生类没有覆盖其基类中的某个虚函数,则该虚函数的行为类似于其他的普通成员,派生类会直接继承其在基类中的版本。
      派生类可以在它覆盖的函数前使用virtual关键字,但不是非得这么做。C++11标准允许派生类显式地注明它使用某个成员覆盖了它继承的虚函数,具体做法是添加一个关键字override。

    派生类对象及派生类向基类的类型转换

    一个派生类对象包含多个组成部分:一个含有派生列自己定义的(非静态)成员的子对象,以及一个与该派生类继承的基类对应的子对象,如果有多个基类,那么这样的子对象也有多个。因此,一个Bulk_quote对象将包含四个数据元素:它从Quote继承而来的bookNo和price数据成员,以及Bulk_quote自己定义的min_qty和discount成员。

    在一个对象中,继承自基类的部分和派生类自定义的部分不一定是连续存储的。

    因为在派生类对象中含有与其基类对应的组成部分,所以我们能把派生类的对象当成基类对象来使用,而且我们也能将基类的指针或引用绑定到派生类对象中的基类部分上。

    Quote item;		// 基类对象
    Buik_quote bulk;	// 派生类对象
    Quote *p = &item;	// p指向Quote对象
    p = &bulk;			// p指向bulk的Quote部分
    Quote &r = bulk;	// r绑定到bulk的Quote部分
    

    这种转换通常称为派生类到基类(derived-to-base)的转换。和其他类型转换一样,编译器会隐式地执行派生类到基类地转换。

    在派生类对象中含有与其基类对应的组成部分,这一事实是继承的关键所在。

    派生类构造函数

    尽管在派生类对象中含有从基类继承而来的成员,但是派生类并不能直接初始化这些成员。和其他创建了基类对象的代码一样,派生类也必须使用基类的构造函数来初始化它的基类部分。

    每个类控制它自己的成员初始化过程。

    Bulk_quote1::Bulk_quote1(const string& book, double p, size_t qty, double disc) :Quote1(book, p), min_qty(qty), discount(disc) {}
    

    该函数将它的前两个参数(分别表示ISBN和价格)传递给Quote的构造函数,由Quote的构造函数负责初始化Bulk_quote的基类部分。当(空的)Quote构造函数体结束后,构建的对象的基类部分也就完成初始化了。接下来初始化由派生类直接定义的min_qty成员和discount成员。最后运行Bulk_quote构造函数的(空的)函数体。
      除非特别指出,否则派生类对象的基类部分会像数据成员一样执行默认初始化。

    首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员。

    派生类使用基类的对象

    派生类可以直接访问基类的公有成员和受保护的成员:

    // 如果达到了购买书籍的某个最低限量值,就可以享受折扣价格了
    double Bulk_quote1::net_price(size_t cnt)const {
    	if (cnt >= min_qty)
    		return cnt * (1 - discount) * price;
    	else
    		return cnt * price;
    }
    

    派生类的作用域嵌套在基类的作用域之内。

    关键概念:遵循基类的接口
      必须明确一点:每个类负责定义各自的接口。要想与类的对象交互必须使用该类的接口,即使这个对象是派生类的基类部分也是如此。
      因此,派生类对象不能直接初始化基类的成员。尽管从语法上来说我们可以在派生类构造函数体内给它的公有或受保护的基类成员赋值,但是最好不要这样做。

    继承与静态成员

    如果基类定义了一个静态成员,则在整个继承体系中只存在该成员的唯一定义。不论从基类中派生出多少个派生类,对于每个静态成员来说都只存在唯一的实例。
      静态成员遵循通用的访问控制规则,如果基类中的成员是private的,则派生类无权访问它。假设某静态成员是可访问的,则我们即可以用过基类使用它也能通过派生类使用它。

    派生类的声明

    派生类的声明与其他类的差别不大,声明中包含类名但是不包含它的派生列表。
      一条声明语句的目的是令程序知晓某个名字的存在以及该名字表示一个什么样的实体。派生列表以及与定义有关的其他细节必须与类的主体一起出现。

    被用作基类的类

    如果我们想将某个类用作基类,则该类必须已经定义而非仅仅声明。
    这一规定的原因显而易见:派生类中包含并且可以使用它从基类继承而来的成员,为了使用这些成员,派生类当然要知道它们是什么。因此该规定还有一层隐含的意思,即一个类不能派生它本身。

    防止继承的发生

    在类名后跟一个关键字final:

    class NoDerived final { /* */ }	// NoDerived不能作为基类
    

    类型转换与继承

      可以将基类的指针或引用绑定到派生类对象上有一层及其重要的含义:当使用基类的引用(或指针)时,实际上我们并不清楚该引用(或指针)所绑定对象的真实类型。

    静态类型与动态类型

      当我们使用存在继承关系的类型时,必须将一个变量或其他表达式的静态类型(static type)与该表达式表示对象的动态类型(dynamic type)区分开来。表达式的静态类型在编译时总是已知的,它是变量声明时的类型或表达式生成的类型;动态类型则是变量或表达式表示的内存中的对象的类型。动态类型直到运行时才可知。
      如果表达式既不是引用也不是指针,则它的动态类型永远与静态类型一致,无论如何都不能改变该变量对应的对象的类型。

    基类的指针或引用的静态类型可以与其动态类型不一致。

    不存在从基类向派生类的隐式类型转换

      之所以存在派生类向基类的类型转换是因为每个派生类对象都包含一个基类部分,而基类的引用或则指针可以绑定到该基类部分上。一个基类的对象既可以以独立的形式存在,也可以作为派生类对象的一部分存在。如果基类对象不是派生类对象的一部分,则它只含有基类定义的成员,而不含有派生类定义的成员。
      因为一个基类的对象可能是派生类对象的一部分,也可能不是,所以不存在从基类向派生类的自动类型转换。
      即使一个基类指针或引用绑定在一个派生类对象上,也不能指向从基类向派生类的转换。

    Bulk_quote bulk;
    Quote *itemP = &bulk;		// 正确:动态类型是Bulk_quote
    Bulk_quote *bulkP = itemP;	// 错误:不能将基类转换成派生类
    

      编译器在编译时无法确定某个特定的转换在运行时是否安全,这是因为编译器只能通过检查指针或引用的静态类型来推断该转换是否合法。
    如果在基类中含有一个或多个虚函数,我们可以使用dynamic_cast请求一个类型转换,该转换的安全检查将在运行时执行。

    Bulk_quote* bulkP = dynamic_cast<Bulk_quote*>(itemP);	// 正确
    

    同样,如果我们已知某个基类向派生类的转换是安全的,则可以使用static_cast来强制覆盖掉编译器的检查工作。

    在对象之间不存在类型转换

      派生类向基类的自动类型转换只对指针或引用类型有效,在派生类类型和基类类型之间不存在这样的转换。
      当初始化或赋值一个类类型的对象时,实际上是在调用某个函数。当执行初始化时,调用构造函数;当执行赋值操作时,调用赋值运算符。这些成员通常都包含一个参数,该参数的类型时类类型的const版本的引用。
      因为这些成员接受引用作为参数,所以派生类向基类的转换允许我们给基类的拷贝、移动操作传递一个派生类的对象。这些操作不是虚函数。当我们给基类的构造函数传递一个派生类对象时,实际运行的构造函数时基类中定义的那个,显然该构造函数只能处理基类自己的成员。

    Bulk_quote bulk;
    Quote item(bulk);		// 使用Quote::Quote(const Quote&)构造函数
    item = bulk;			// 调用Quote::operator=(const Quote&)
    

    在上述过程中会忽略Bulk_quote部分,所以可以说bulk的Bulk_quote部分被切掉(sliced down)了。

    当我们用一个派生类对象为一个基类对象初始化或赋值时,只有该派生类对象中的基类部分会被拷贝,移动或赋值,它的派生类部分将被忽略掉。

    展开全文
  • 15.2定义基类和派生类

    2021-11-03 21:16:54
    定义基类和派生类 我们首先完成基类和派生类的定义: 我们首先定义了一个名为Quote的类作为基类表示按原价销售的书籍,Bulk_quote的类作为派生类表示可以打折销售的书籍。这些类包含两个函数:isbn()表示书籍的ISBN...

    定义基类和派生类

    我们首先完成基类和派生类的定义:

    我们首先定义了一个名为Quote的类作为基类表示按原价销售的书籍,Bulk_quote的类作为派生类表示可以打折销售的书籍。这些类包含两个函数:isbn()表示书籍的ISBN编号,netPrice()表示实际出售的价格。

    class Quote {
    public:
        Quote() = default;
        Quote(const string &s, double d):_isbn(s), _price(d){}
        virtual double netPrice(int n) const {
            return _price * n;
        }
    private:
        string _isbn = "x";
    protected:
        double _price = 0.0;
    };
    class BulkQuote : public Quote { //公共继承了Quote
    public:
        BulkQuote() = default;
        BulkQuote(const string &s, double d, int b, double dis):
    		Quote(s, d), _minBuy(b), _discount(dis){}
        double netPrice(int n) const override {
            if (n > _minBuy) {
                return n * _discount * _price;
            }
            return n * _price;
        }
    private:
        int _minBuy;         //最低购买数量
        double _discount;    //小数表示的折扣额
    };
    

    1.定义基类

    基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。

    ①基类的两种成员函数:

    基类希望派生类进行覆盖的函数(虚函数):

    • 当我使用指针或引用调用虚函数时,该调用会被动态绑定:根据引用或指针所绑定的对象类型,调用可能执行基类的版本,也可能执行某个派生类的版本。
    • 虚函数是在成员函数声明语句之前加上关键字virtual使得该函数执行动态绑定。virtual只能出现在类内部的声明语句之前,而不能用于类外部的函数定义。
    • 虚函数解析过程发生在运行时。

    基类希望派生类直接继承的函数:

    • 静态成员函数解析发生在编译时。

    ②访问控制与继承

    派生类可以继承定义在基类中的成员,但是不一定有权访问从基类继承而来的成员。派生类可以访问基类的公有和保护成员而不能访问基类的私有成员

    2.定义派生类

    ①派生类中的虚函数

    派生类经常覆盖它继承的虚函数,但并非必须覆盖,如果没有覆盖其继承基类的虚函数,则类似其他普通成员,派生类会直接继承在基类的版本。

    派生类用关键字override显式地定义覆盖了地虚函数。

    ②派生类对象及派生类向基类的类型转换

    一个派生类包含多个组成部分:

    • 派生类自己定义的成员的子对象。
    • 与派生类继承的基类对应的子对象。

    如:BulkQuote包含自己定义的子对象_minBuy、_discount和从基类Quote继承的子对象_isbn和_price。

    因为派生类对象中含有与其基类对应的组成部分,所以我们能把派生类的对象当成基类对象来使用,也能将基类的指针或引用绑定到派生类对象中的基类部分上。这种转换称为派生类到基类的类型转换。编译器会隐式的执行派生类到基类的转换。

    ③派生类构造函数

    派生类从基类继承了基类的构造函数,派生类对象不能直接初始化基类的成员,而是应该通过基类的接口(基类的构造函数)初始化从基类继承而来的成员

    如:BulkQuote的构造函数

    BulkQuote(const string &s, double d, int b, double dis):
    	Quote(s, d), _minBuy(b), _discount(dis){}
    

    将其前两个参数传递给Quote的构造函数,由Quote初始化BulkQuote的基类部分,接下来初始化由派生类直接定义的成员。

    ④派生类的声明

    派生类的声明与其他类差别不大,声明中包含类名,但是不包含它的派生列表

    class BulkQuote : public Quote; //错误:声明不能包含派生列表
    class BulkQuote; //正确:声明派生类的正确方式
    

    声明语句的目的是令程序知晓某个名字的存在以及该名字表示一个什么样的实体,如一个类、一个函数或一个变量等。派生列表以及预定义有关的其他细节必须与类的定义一起出现。

    ⑤被用作基类的类

    如果我们向将某个类用作基类,则该类必须已经定义而非仅仅声明:

    class Quote;   //声明但未定义
    class BulkQuote : public Quote {...} //错误:Quote必须已经定义
    

    ⑥防止继承的发生

    有时我们会定义这样一种类,我们不希望其他类继承它,或者不想考虑他是否适合做一个基类。只要我们在这种类名后面加上一个final:

    class NoDerived final {...} //NoDerived不能用作基类
    
    
    展开全文
  • JAVA基类和派生类

    2021-02-13 01:06:04
    从外部看来,派生类是一个与基类具有相同接口的新类,或许还会有一些额外的的方法和域 。但继承并不仅仅是类的复用。当创建了一个派生类的对象时,该类包含了一个基类的子对象。这个子对象你用基类直接创建的对象...

    从外部看来,派生类是一个与基类具有相同接口的新类,或许还会有一些额外的的方法和域 。但继承并不仅仅是类的复用。当创建了一个派生类的对象时,该类包含了一个基类的子对象。这个子对象和你用基类直接创建的对象没有什么两样。二者的区别在于,后者来自于外部,而基类的子对象来自于派生类对象的内部。对基类的子对象初始化时至关重要的,而且也只有一种方法来保证这一点,那就是在派生类的构造器中调用基类的构造器,而基类的构造器具有执行基类初始化所需的所有能力和知识。

    在无参构造器时, java会自动在派生类的构造器中插入对基类的构造器的调用。

    public class Humans {

    Humans(){

    System.out.println("我是人!");

    }

    }

    public class Student extends Humans{

    Student(){

    System.out.println("我是学生!");

    }

    }

    public class test {

    public static void main(String args[]){

    new Student();

    }

    }

    输出结果为:

    我是人!

    我是学生!

    可以发现,总是基类的构造器先被初始化。

    但是当构造器有参数时,那就必须使用关键字super现实地编写调用基类构造器的代码,并且匹配适当的参数列表。

    public class Humans {

    private String name;

    Humans(String name){

    System.out.println("我是叫"+name+"的人");

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    }

    public class Student extends Humans{

    private String name;

    Student(String name){

    super(name);

    System.out.println("我是学生!");

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    }

    public class test {

    public static void main(String args[]){

    new Student("zhangsan");

    }

    }

    输出结果:

    我是叫zhangsan的人

    我是学生!

    如果注释掉上面的super(name);将会报错。原因是派生类必须调用基类构造器。因为实例化派生类时,基类也会被实例化,如果不调用基类的构造器,基类将不会被实例化,所以派生类没有调用基类构造器会报错。

    但是如果Humans的代码变成这样就不会错。如下代码:

    public class Humans {

    private String name;

    Humans(){

    System.out.println("我是人!");

    }

    Humans(String name){

    System.out.println("我是叫"+name+"的人");

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    }

    public class Student extends Humans{

    private String name;

    Student(String name){

    //super(name);

    System.out.println("我是学生!");

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    }

    public class test {

    public static void main(String args[]){

    new Student("zhangsan");

    }

    }

    输出结果为:

    我是人!

    我是学生!

    原因是,如果基类有一个无参的构造器,就算派生类不用super显示调用基类的构造函数,编译器也会自动

    去调用基类的无参构造函数。

    所以上面的代码不会报错,输出结果也不是

    我是叫zhangsan的人

    我是学生!

    而是

    我是人!

    我是学生!

    派生类继承了基类的所有public和protected属性和方法,代码如下:

    public class Humans {

    public String sex;

    protected int age ;

    private String name;

    Humans(String sex,String name,int age){

    this.sex = sex;

    this.name = name;

    this.age = age;

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    }

    public class Student extends Humans{

    Student(String sex ,String name,int age){

    super(sex,name,age);

    }

    }

    public class test {

    public static void main(String args[]){

    Student s = new Student("男","zhangsan",10);

    System.out.println(s.sex);

    System.out.println(s.name);

    System.out.println(s.age);

    }

    }

    上面的System.out.println(s.name);会报错,因为name是private属性,如需访问,采用get方法:

    System.out.println(s.getName());

    输出结果为:

    zhangsan

    10

    如果派生类定义了和基类一样的属性或方法,将覆盖基类的属性和方法。如将student改为如下代码:

    public class Student extends Humans{

    public String sex;

    protected int age ;

    private String name;

    Student(String sex ,String name,int age){

    super(sex,name,age);

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    }

    输出结果为:

    null

    null

    0

    因为只有基类的属性在构造时赋值了,派生类的没有,当访问这些属性时,访问的是派生类的属性,所以全为null或者0。

    只有当派生类的属性也被实例化时,才会得到属性的值。代码改为如下:

    public class Student extends Humans{

    public String sex;

    protected int age ;

    private String name;

    Student(String sex ,String name,int age){

    super(sex,name,age);

    this.sex = sex;

    this.name = name;

    this.age = age;

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    }

    输出结果为:

    zhangsan

    10

    要注意的是,super必须在构造器的最前面,不然会报错。

    展开全文
  • c++基类和派生类析构顺序

    千次阅读 2019-03-05 16:41:40
    class father{ public: father() { qDebug(); } ~father() { qDebug();...new : 先调用 基类的 构造 一层一层的调用 ...delete: 顺序同new反过来 先调用派生类的析构 在 基类的 析构 一层一层的
  • c++基类和派生类

    千次阅读 2017-01-16 23:52:14
    实际上这是没有必要的,因为狗是哺乳动物的一种特殊动物,定义狗可以沿用哺乳动物的所需的属性方法,只需加上狗的颜色属性狗尾巴会摇方法即可。 继承是C++语言的一种重要机制,它允许在
  • 派生类和基类中各自实现一个内容不同但函数名和参数类型完全相同的方法时,会怎样? 答案是:基类对象调用的是基类的方法,派生类对象调用执行的是派生类中重新提供的方法。 这种派生类中同名同参方法替代掉基类...
  • 基类和派生类的构造析构顺序

    千次阅读 2018-05-31 17:08:07
    构造:先基类,后派生类析构:先派生类,后基类同时:无需手动析构基类,在delete派生类的同时会析构基类
  • 定义基类和派生类

    2019-11-05 08:35:41
    继承动态绑定对程序的编号有两方面的影响:一是我们可以更容易地定义与其它相似但不完全相同的;二是在使用这些彼此相似的编写程序时,我们可以在一定程度上忽略掉它们的区别。 定义鸡肋: #include<bits...
  • 构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。 在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的...
  • 今天重温C++的知识,当看到虚基类这点的时候,那时候也...(1)、obj 是一个虚拟继承的派生类的对象 (2)、mem是从虚拟基类派生下来的成员 (3)、p是基类类型的指针 当这种情况下,p->mem会比obj.mem多了两个中间层
  • 创建派生类对象时,构造函数的执行顺序继承顺序相同,即先执行基类构造函数,再执行派生类构造函数。 而销毁派生类对象时,析构函数的执行顺序继承顺序相反,即先执行派生类析构函数,再执行基类析构函数。 请看...
  • 在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。 解决办法:在派生类的...
  • 深剖基类和派生类的虚函数表

    千次阅读 2018-03-31 15:31:53
    1、当派生类实现基类的虚函数时,基类中虚函数表虚函数地址和派生类中虚函数表虚函数地址不同;当派生类不实现基类的虚函数时,基类中虚函数表虚函数地址和派生类中虚函数表虚函数的地址相同。1、派生类重新...
  • Java基类和派生类

    2021-02-12 13:34:56
    从外部看来,派生类是一个与基类具有相同接口的新类,或许还会有一些额外的的方法和域 。但继承并不仅仅是类的复用。当创建了一个派生类的对象时,该类包含了一个基类的子对象。这个子对象你用基类直接创建的对象...
  • C++基类和派生类

    千次阅读 2016-10-11 19:13:27
    一个基类派生出一个派生类,它又做另一个派生类的基类,则原来基类为该派生类的间接基类。 基类与派生类之间的关系可以有如下几种描述: 1 派生类是基类的具体化 类的层次通常反映了客观世界中某种真实
  • 派生类公有继承于基类 则在定义派生类对象时,程序会先调用基类构造函数,然后再调用派生类构造函数。 #include <iostream> class node{ private: int a; public: int b; node(); ~node(){} }; node::...
  • 根据圆的原新位置半径算面积,根据正方形中心定点坐标算面积
  • Java-基类和派生类

    千次阅读 2017-04-05 17:43:21
    派生类是继承基类的(extends) 派生类调用基类的普通方法时: (1)super.方法名();(基类中的方法) (2)方法名(); 派生类调用基类中的构造方法时: (1)无参数的构造方法:  不用进行显式的调用,直接...
  • 文章目录定义基类派生类多基派生虚基类派生类的构造函数派生类的析构函数 定义基类派生类 class 派生类名:派生方式 基类名 { private: 新增私有成员列表; public: 新增公开成员列表; }; 三种数据成员...
  • #include using namespace std;class Base { public: Base::Base() { cout 基类构造函数" ; } Base::~Base() { cout 基类析构函数" ; } }; class Son
  • 写一个基类Shape,再写两个派生类:rect,circle,要求基类和派生类除了有各自的构造函数以外,还有求面积的成员函数。主函数中定义不同的派生类对象,求不同对象的面积。 代码: #include <iostream> #...
  • 基类派生类

    2012-04-02 21:35:51
    基类派生类
  • 基类和派生类的对象间不存在隐式类型转换。当我们初始化或者赋值一个类类型对象时,实际上是在调用某个函数。当执行初始化时,调用构造函数;当执行赋值操作时,调用赋值运算符。 故:使用派生类基类初始化或赋值...
  • java基类派生类

    2021-12-19 20:56:11
    基类是父类,派生类是子类。 //B叫做父类或者基类,A叫做子类或者派生类 class A extends B{ }
  • 基类和派生类,虚函数

    千次阅读 2016-11-26 22:22:06
    基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自持有的成员。
  • 基类和派生类 面向对象程序设计 C++ 实验报告
  • 1.可以使用派生类指针初始化基类指针,同样也可以使用派生类的引用或对象初始化基类的引用,注意这里提到的是对象的指针引用而不是对象本身; (但是,在使用基类指针(或引用)指向(或引用)派生类对象时需要注意...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 187,570
精华内容 75,028
关键字:

基类和派生类

友情链接: lpc3250_uboot_i2c.zip