精华内容
下载资源
问答
  • 一、父类指针指向子类对象 先上代码: class Base { public: void base_fun1() {} virtual void base_fun2() { cout << "this is Base fun2()" << endl; } public: int a_a; protected: int a...

    一、父类指针指向子类对象

    先上代码:

    class Base 
    {
    public:
    	void base_fun1() {}
    	virtual void base_fun2() 
    	{
    		cout << "this is Base fun2()" << endl;
    	}
    public:
    	int a_a;
    protected:
    	int a_b;
    private:
    	int a_c;
    };
    
    class Son :public Base 
    {
    public:
    	void son_fun1() {}
    	void base_fun2()//重写父类虚函数
    	{
    		cout << "this is Son fun2()" << endl;
    	}
    public:
    	int b_a;
    protected:
    	int b_b;
    private:
    	int b_c;
    };
    
    int main()
    {
    	Base* ba = new Son();//父类指针指向子类
    
    	//父类指针只能访问从父类继承而来的成员变量和成员函数
    	//只能访问从父类继承而来的成员变量
    	ba->a_a = 10;
    	//只能访问从父类继承过来的成员函数
    	ba->base_fun1();
    	//由于子类对象用的是从父类继承而来的虚函数表,所以父类指针可以通过查找父类虚函数表的方式,调用被子类重写后的函数
    	ba->base_fun2();
    
    	//子类指针对所有的成员变量和成员函数随意访问(受访问权限限制)
    	Son* so = new Son();//子类指针指向子类
    	so->a_a = 10;
    	so->b_a = 10;
    	so->base_fun1();
    	so->son_fun1();
    	so->base_fun2();
    	so->Base::base_fun2();
    
    	return 0;
    }
    

    so是子类指针,指向子类对象,可以通过so访问任何子类的东西。前提是在遵守访问权限限制的情况下。
    ba是父类指针,指向子类对象,但是只能通过ba访问继承过来的成员变量和成员函数。也就是说,不能通过指向子类对象的的父类指针访问子类对象的本身的成员变量和成员函数。
    ba可以访问子类对父类虚函数重写的函数。因为子类和父类共用一个虚函数表,子类用的是从父类继承过来的虚函数表(虚函数表问题点击此处),父类指针也只是通过父类的虚函数表查找函数地址,然后调用,之所以可以通过父类指针访问子类重写父类虚函数后的函数,根本上还是因为子类用的还是父类的虚函数表。
    通过让父类指针和子类指针指向同一个子类对象,看一下这俩指针到底指向了哪里。我们都知道子类对象是个大空间,里面包括子类本身的成员变量,还包含一个小空间,小空间是父类的无名对象,被子类对象所拥有。如果没有虚继承的话,小空间的首地址和大空间的首地址是一起的,导致无法查看父类指针和子类指针的指向区别,如下图所示(对于继承和虚继承博客,点击此处):
    在这里插入图片描述
    但是虚继承就不一样了,虚继承的话,子类的本身变量成员在小空间上方。如下虚继承代码和子类对象空间结构图:
    代码:

    class Base 
    {
    public:
    	void base_fun1() {}
    	virtual void base_fun2() 
    	{
    		cout << "this is Base fun2()" << endl;
    	}
    public:
    	int a_a;
    protected:
    	int a_b;
    private:
    	int a_c;
    };
    
    class Son :virtual public Base 
    {
    public:
    	void son_fun1() {}
    	void base_fun2()//重写父类虚函数
    	{
    		cout << "this is Son fun2()" << endl;
    	}
    public:
    	int b_a;
    protected:
    	int b_b;
    private:
    	int b_c;
    };
    
    int main()
    {
    	Son so;
    	Base* baptr = &so;
    	Son* soptr = &so;
    	cout << "父类指针指向的空间首地址:" << (int)baptr << endl; 
    	cout << "子类指针指向的空间首地址:" << (int)soptr << endl;
    	cout << "a_a的地址:" << (int)&so.a_a << "   " << "b_a的地址:" << (int)&so.b_a << endl;
    	return 0;
    }
    
    

    运行结果:
    在这里插入图片描述
    子类对象空间结构图 :
    在这里插入图片描述
    所有的东西都在上面的图中。(可以通过vs的开发者工具查看类的对象的空间结构图)
    上面的图展示了子类对象的空间结构图,含有指向子类对象的父类指针指向的空间,和指向子类对象的子类指针指向的空间。所以两个指针指向的空间的首地址不一样,指向子类对象的子类指针指向的空间包含指向子类对象的父类指针指向的空间。而父类指针指向的空间里面的东西,就是从父类继承而来的东西,所以只能通过父类指针访问从父类继承过来的成员变量,而能访问子类对象重写的函数,是因为子类用的是从父类继承过来的虚函数指针,用的虚函数表也是从父类继承而来的,父类指针可以通过父类的虚函数指针访问虚函数表,进而调用子类重写的函数。
    当子类用保护继承或者私有继承的方式继承父类时,编译器就不允许父类指针指向子类对象了。因为父类指针本质上是指向子类对象空间里的从父类继承而来的那部分内容空间,而这部分内容用的是私有或者包含继承的方式,不允许随意访问,自然也不允许外部指针随意指向。但是在子类类内(成员函数内)可以,因为类内不受访问权限的限制,可以随意访问这些东西。
    总结:
    如果父类指针指向子类对象,那么只可以通过父类指针访问子类继承的父类的成员函数和成员变量。(遵守访问权限限制是前提,其次,由于子类继承了父类的虚函数指针,且子类用的也是父类的虚函数指针和虚函数表,所以父类指针可以通过查找父类虚函数表的方式调用被子类重写后的函数)。

    二、父类对象和子类对象之间的问题

    子类对象可以直接赋值给父类对象
    子类对象可以直接初始化父类对象
    但是反过来不行。
    代码:

    class Base 
    {
    public:
    	void base_fun1() {}
    	virtual void base_fun2() 
    	{
    		cout << "this is Base fun2()" << endl;
    	}
    public:
    	int a_a;
    protected:
    	int a_b;
    private:
    	int a_c;
    };
    
    class Son :virtual public Base 
    {
    public:
    	void son_fun1() 
    	{
    		Son so;
    		Base* baptr = &so;
    	}
    	void base_fun2()//重写父类虚函数
    	{
    		cout << "this is Son fun2()" << endl;
    	}
    public:
    	int b_a;
    protected:
    	int b_b;
    private:
    	int b_c;
    };
    
    int main()
    {
    	Base a;
    	Son b;
    
    	a = b;
    	b = a;//error
    	return 0;
    }
    

    编译器会自动生成赋值函数,这个赋值函数和缺省拷贝构造函数可以说是做法一模一样。都是值拷贝(字节拷贝)。所以如果父类有指针而对象用继承的指针指向了堆空间的话,一定小心这个赋值语句,会造成两个指针指向一个堆空间的问题,这个时候要小心析构函数对一个堆空间进行两次释放的问题。这里不对这个问题再次进行讨论了。在构造函数的博客以及多态的博客中都有讲述产生的问题和解决办法。
    为什么子类对象可以赋值给父类对象,而父类对象不能赋值给子类对象呢?这个和本博客的第一大点也有关系,因为可以把子类对象强制转换成父类类型,而这个代价就是,只能访问子类从父类继承的东西,即子类对象会退化成父类对象。换句话说,就是子类对象的大空间中包含的小空间(存放着从父类继承的成员变量)供你强制转换后使用。
    而父类对象不能被强制转换成子类类型,所以自然也不能将父类对象赋值给子类对象,也不能通过父类对象对子类对象进行初始化,或者拷贝构造。因为这些函数都有this指针,要通过的第一关就是必须得允许this指针指向你,也就是将你强制转换成等号左边的类型。第一关就通不过,所以肯定不能这样操作。

    展开全文
  • 利用多态机制,一个指向子类的父类指针或引用,当调用被子类重写的虚函数时,实际上调用的是子类函数,这是通过多态的方式来实现父类调用子类,该方法需要一个引用或者指针调用虚函数来实现。如下面所示: Class ...

    父子类关系

    对于继承关系中的父类和子类,我们可以说子类是父类的一种,子类继承了父类的属性和行为。因此,子类可以访问父类的所有非私有成员。相反,父类一般情况下是不能访问子类成员的。然而,我们可以通过一些方法间接的实现父类访问子类,即父类函数访问子类函数。

    方法一

    利用多态机制,一个指向子类的父类指针或引用,当调用被子类重写的虚函数时,实际上调用的是子类函数,这是通过多态的方式来实现父类调用子类,该方法需要一个引用或者指针调用虚函数来实现。如下面所示:

    Class Base
    {
    public:
        virtual void fun()
        {
            std::cout << "Base::fun()" << std::endl;
        }
    };
    
    class Derived : public Base
    {
    public:
        virtual void fun()
        {
            std::cout << "Derived::fun()" << std::endl;
        }
    };
    
    Base* base = new Derived;
    base->fun();    //该结果输出为: "Derived::fun()"

    上面简单的例子就是通过多态实现的父类调用子类成员函数,这里面必须要有虚函数,并且在子类里面重写。

    方法二

    通过函数指针同样可以实现父类函数访问子类函数,示例代码如下:

    class Base
    {
    public:
        typedef void (Base::*pfn)();
    
        void CallFunction()
        {
            if(fn != NULL)
            {
                (this->*fn)();  //调用Derived类中的fun()函数
            }
        }
        void SetPfn(pfn fn)
        {
            this->fn = fn;
        }
    
    private:
        pfn fn;
    
    };
    
    class Derived : public Base
    {
    public:
        void Init()
        {
            SetPfn((pfn)&Derived::fun);
        }
    
        void fun()
        {
            std::cout << "Derived::fun be called in Base function!" << std::endl;
        }
    
    };
    
    Derived derived;
    derived.Init();
    derived.CallFunction();//这里调用的是父类的成员函数,该函数通过函数指针调用了子类的普通成员函数,调用结果输出:"Derived::fun be called in Base function!",即父类成员函数调用了子类成员函数。
    
    

    程序执行结果如下:

    这里写图片描述

    展开全文
  •  其实这块我们需要分为两种情况来考虑,第一种是类继承(静态绑定),第二种是父类中包含虚函数(动态绑定)。 2 具体实现 2.1 没有虚函数的继承  如果以一个基础类指针指向一个派生类对象,那么经由该指针只能...

    1 前言

    记得当初阿里面试的时候被问道这个问题,平时自己面对这个方法都习以为常的使用,C++多态和动态绑定不就是这么实现的嘛,但是还真没有刻意去关注其中的原理。今天特意看了相关资料,现在分享给大家。
      其实这块我们需要分为两种情况来考虑,第一种是类继承(静态绑定),第二种是父类中包含虚函数(动态绑定)。

    2 具体实现

    2.1 没有虚函数的继承
      如果以一个基础类指针指向一个派生类对象,那么经由该指针只能访问基础类定义的函数(静态绑定)。
      如果以一个派生类类指针指向一个基础类对象,必须先做强制转型动作(explicit cast),这种做法很危险,也不符合生活习惯,在程序设计上也会给程序员带来困扰。(一般不会这么去使用)
      如果基础类和派生类类定义了相同名称的成员函数,那么通过对象指针调用成员函数时,到底调用那个函数要根据指针的原型来确定,而不是根据指针实际指向的对象类型确定。(指针类型是谁就调用谁)
      这块我需要说明一下为何基础类可以指向派生类对象,而派生类不去指向父类对象。
      通常来说,子类总是含有一些父类没有的成员变量,或者方法函数。而子类肯定含有父类所有的成员变量和方法函数。所以用父类指针指向子类时,没有问题,因为父类有的,子类都有,不会出现非法访问问题。
      但是如果用子类指针指向父类的话,一旦访问子类特有的方法函数或者成员变量,就会出现非法,因为被子类指针指向的由父类创建的对象,根本没有要访问的那些内容,那些是子类特有的,只有用子类初始化对象时才会有。
     2.2 包含虚函数的继承
      有虚函数的继承,那么父类指针指向子类对象就是我们常见的多态实现,也就是动态绑定。
      虚拟函数就是为了对“如果你以一个基础类指针指向一个衍生类对象,那么通过该指针,你只能访问基础类定义的成员函数”这条规则反其道而行之的设计。
      重点:
      当然这里还包括纯虚拟函数,只要是拥有纯虚拟函数的类,就是抽象类,它们是不能够被实例化的(只能被继承)。如果一个继承类没有改写父类中的纯虚函数,那么他也是抽象类,也不能被实例化。抽象类不能被实例化,不过我们可以拥有指向抽象类的指针,以便于操纵各个衍生类。

    3 总结

    当定义一个指向子类实例的父类指针的时候,内存中实例化了子类,由于子类继承了父类,因此内存中的子类里包含父类的所有成员。但由于申明的是父类指针,因此该指针不能够访问子类的成员,而只能访问父类的成员。然而在父类里可以声明纯虚函数和定义虚函数,使用父类指针访问**虚函数或纯虚函数**的时候,访问到的是子类里重写的函数。当然,对于虚函数,如果子类里没有对其重写的话,仍然访问到父类里定义的虚函数。可见虚函数和纯虚函数的却别仅仅在于:纯虚函数没有定义,只有声明。
      
      区别动态绑定与静态绑定

    参考文献:
      https://www.zhihu.com/question/31345300
      http://blog.csdn.net/zhouwei1221q/article/details/47953995
      http://www.cnblogs.com/zhangbaochong/p/5380016.html

    展开全文
  • 结论:父类指针可以指向子类对象,但是只能访问父类成员,不能访问子类成员 结论:子类指针不可以指向父类对象,需要类型转换才可以 笔记: <1>.当基类指针指向派生类的时候,只能操作派生类从基类中继承...

    结论:父类指针可以指向子类对象,但是只能访问父类成员,不能访问子类成员

    结论:子类指针不可以指向父类对象,需要类型转换才可以

     

    笔记:
    <1>.当基类指针指向派生类的时候,只能操作派生类从基类中继承过来的数据.
    <2>.当派生类指向基类的指针,因为内存空间比基类长,访问的话会导致内存溢出,所以不允许派生类的指针指向基类。

    加深理解:
    <1>.通常来说,子类肯定含有父类所有的成员变量和方法函数.所以用父类指针指向子类时,没有问题
    ,因为父类有的,子类都有,不会出现非法访问问题.
    <2>.如果用子类指针指向父类的话,一旦访问子类特有的方法函数或者成员变量(基类是没有的),就会出现非法访问;
    因为被子类指针指向的由父类创建的对象,根本没有要访问的那些内容,那些是子类特有的,只有用子类初始化对象时才会有.

    通过 static_cast、dynamic_cast可以解决这个问题
    1 static_cast
    用法:static_cast < type-id > ( exdivssion )
    该运算符把exdivssion转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
    ①用于类层次结构中基类和子类之间指针或引用的转换。
      进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
      进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
    ②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
    ③把空指针转换成目标类型的空指针。
    ④把任何类型的表达式转换成void类型。

    注意:static_cast不能转换掉exdivssion的const、volitale、或者__unaligned属性。

    2 dynamic_cast
    用法:dynamic_cast < type-id > ( exdivssion )
    该运算符把exdivssion转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;
    如果type-id是类指针类型,那么exdivssion也必须是一个指针,如果type-id是一个引用,那么exdivssion也必须是一个引用。

    dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
    在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
    在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
    ————————————————
    版权声明:本文为CSDN博主「C-mac」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_34673519/article/details/105247122

     

    展开全文
  • } //指针方法 (缺少调用子类的析构函数) //call AnimalC //call CatC //Tom小猫在说话! //call ~AnimalC void inherit_09_pointer() { cout 指针方法" ; AnimalC* cat = new CatC("Tom"); cat->spead(); // ...
  • c++父类调用子类的方法

    千次阅读 2021-05-31 17:22:42
    在c++中,父类和子类虚函数都有对应的虚函数表,如果子类重写了虚函数,就会将新写的这个函数的地址放到他自己的表内,并且通过父类指针指向子类所开辟的空间,这样就可以调用子类的方法 方法二:强转转换 但是有...
  • 文章目录虚析构函数父类指针指向子类对象||子类指针指向父类对象总结: 虚析构函数 《Effective C++ 》 当派生类(derived class)对象由一个基类(base class)指针删除时,若基类有一个非虚函数(non-virtual)...
  • 之前一直以为 this关键字 是指调用者对象,但是这次才真正理解,this代表当前对象,但是指向调用者对象,其实就是多态的用法,如下所示:B 继承了 A,在B 中调用A类的方法,在A 中用this 访问成员变量和方法,此时,...
  • 索引实例化概念定义形式为什么将父类指针子类实例化?目的——实现多态程序代码实现多态 code c++运行结果结论 实例化概念 定义 实例化是指在面向对象的编程中,把用类创建对象的过程称为实例化。是将一个抽象的...
  • 父类指针指向子类对象 子类指针指向父类对象 多态 c++默认根据指针类型调用对应的函数 非多态demo c++中的多态通过虚函数实现(virtual修饰) 虚函数实现demo 虚函数实现原理 汇编分析 内存分析 父类...
  • 关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类指针调用实际子类成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来...
  • } JAVA:使用super调用父类的读域函数getAge() Person.java package test; public class Person { public Person(int age) { this.aAge = age; } public int getAge() { return aAge; } public void setAge(int age)...
  • 继承是为了重用父类代码,同时为实现多态性作准备。那么什么是多态呢?方法的重写、重载与动态连接构成多态性。Java之所以引入多态的概念,原因之一是它在类的继承问题上和C++不同,后者允许多继承,这确实给其带来...
  • 子类调用父类方法

    千次阅读 2021-01-12 19:09:51
    当有相同的属性或者方法是this子类表示调用自己的属性或者方法,super调用父类的属性或者方法。当子类调用的属性方法在子类中不存在时,那么this和super都是调用父类的属性或者方法1.方式一子类调用父类的方法,包含...
  • 这里写自定义目录标题父类指针指向子类对象基类中没有定义虚函数时当基类中定义虚函数时 父类指针指向子类对象 ...3.如果基础类和衍生类定义了相同名称的成员函数,那么通过对象指针调用成员函数时,到底调用那个函数
  • 继承是为了重用父类代码,同时为实现多态性作准备。那么什么是多态呢?方法的重写、重载与动态连接构成多态性。Java之所以引入多态的概念,原因之一是它在类的继承问题上和C++不同,后者允许多继承,这确实给其带来...
  • 一种便捷的初始化成员变量的方式。 只能用在构造函数中 初始化顺序只跟成员变量的声明顺序有关 struct Person { int m_age; int m_height; Person(int age ,int height) { m_age = age; m_height = ...
  • c++ 子类对象调用父类的方法

    千次阅读 2021-03-08 20:37:26
    第一种方式就是子类重写父类的方法,在子类中定义和父类同样的方法,方法体中调用父类的方法 父类代码 #pragma once #include "CClientSocket.h" class CTCPInterface { public: CTCPInterface(); ~...
  • 展开全部这个是我很早之前学习到“多态”时候整理的笔记。送你了!希望对你有用!62616964757a686964616fe4b893e5b19e...继承是为了重用父类代码,同时为实现多态性作准备。那么什么是多态呢?方法的重写...
  • Java多态性详解 (父类引用子类对象)

    千次阅读 2021-02-25 20:34:44
    继承是为了重用父类代码,同时为实现多态性作准备。那么什么是多态呢?方法的重写、重载与动态连接构成多态性。Java之所以引入多态的概念,原因之一是它在类的继承问题上和C++不同,后者允许多继承,这确实给其带来...
  • 成员变量的隐藏和方法的重写Goods.javapublic class Goods {public double weight;public void oldSetWeight(double w) {weight=w;System.out.println("double型的weight="+weight);}public double oldGetPrice() {...
  • c++知识细节-基类指针指向子类对象/虚纯虚函数/多态性...Q: 既然父类指针没有办法调用子类成员函数,那么为什么还让父类指针指向子类对象呢? 虚函数 Q: 有没有一个解决方法,使我们只定义一个对象指针,就能调用各种子类
  • 【简答题】汽车核保的主要内容【判断题】在java中产生子类对象时,直接调用子类的构造方法而不调用父类的构造方法。【单选题】广域网一般采用的传播方式是( )。 (10.0分)【名词解释】网络安全【填空题】计算机病毒的...
  • 展开全部这个是我很e5a48de588b63231313335323631343130323136353331333264643162早之前学习到“多态”时候整理的笔记。送你了!希望对你有用!...继承是为了重用父类代码,同时为实现多态性作准备。那么什么...
  • 因此,在创建子类对象时,为了初始化从父类继承来的数据成员,系统需要调用其父类的构造方法。如果没有显式的构造函数,编译器会给一个默认的构造函数,并且该默认的构造函数仅仅在没有显式地声明构造函数情况下创建...
  • 一句话总结就是基类的指针或者引用可以访问到实际指向的子类的函数。 多态的实现 前提是该函数在在基类中必须是虚函数,在子类中必须重写(函数的声明完全一致,参数以及返回值完全类型一致) 多态的原理 一句话总结...
  • #父类#package wen.jianbao.test;public class Father {public String name = "我是父类的name";public void show() {System.out.println(this);...}}#子类#package wen.jianbao.test;public clas...
  • 如果基础类和衍生类定义了相同名称的成员函数(包括参数相同),那么通过对象指针调用成员函数时,到底调用那个函数要根据指针的原型来确定,而不是根据指针实际指向的对象类型确定。 虚函数就是为了对.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 65,118
精华内容 26,047
关键字:

父类指针调用子类成员