精华内容
下载资源
问答
  • Stu类继承了Per类,然后我实例化了两个类的对象,接着我将子类的stu对象赋给父类的per对象 随后我使用hashCode()方法打印出了per对象刚刚生成的时候以及被赋值给stu对象之后的内存地址,发现两个内存地址是不相...

    前言

    今天在复习Java的时候无意间用到了getClass方法,不过发现了一点以前没有发现过的问题,在这里记录一下。

    问题描述

    我写了两个类一个叫做Per另一个叫做Stu
    在这里插入图片描述在这里插入图片描述
    Stu类继承了Per类,然后我实例化了两个类的对象,接着我将子类的stu对象赋给父类的per对象
    在这里插入图片描述
    随后我使用hashCode()方法打印出了per对象刚刚生成的时候以及被赋值给stu对象之后的内存地址,发现两个内存地址是不相同的,这就说明per这个引用已经不再指向原来的那个对象的地址空间,而是指向了新的对象地址空间,其实指向的就是stu对象的地址空间。

    不过这里有一个问题是:既然per指向了stu对象的地址空间那per应该是Stu类型的,并且可以调用Stu类中的方法。但是我做了一下测试发现不可以
    在这里插入图片描述
    可是这个时候发生一个很有意思的事情,当我用编译器查看per对象的类型时候,编译器给出的是
    在这里插入图片描述
    可能这也就是为什么per不能调用Stu类中的方法的原因。
    这个时候编译器依然认为per是Per类型的,当我使用per的getClass()方法时候,输出结果如下
    在这里插入图片描述
    我们可以看出输出类名是Stu的。

    解析

    当看到这个的时候我百思不得其解,编译器认为per是Per类型的,而获得到的class名字却是Stu,于是我尝试着去看了一下源码:
    在这里插入图片描述
    我把官方给的例子运行了一下,发现getClass()的运行机制的确如此
    在这里插入图片描述
    从这里我们看出n打印出来的class是Integer,可以知道的是Integer是继承Number的。这样一来就和我上面自己描述的情况完全一致。在Number n = 0;这条语句执行之后n相当于被隐式的转换成了Number的子类Integer类型的。

    总结

    虽然Java的官方案例证明了这一点是合理的,不过我还是没有去研究它的底层机制到底是为什么,另外还有一个问题就是为什么父类对象被被子类对象赋值之后类型依然是父类类型的。

    展开全文
  • 类的继承以及对象的赋值会带来成员变量的相互传递。...对象赋值带来的成员变量的传递采用,实函数采用数据类型的实函数,虚函数采用赋值源的虚函数,成员变量采用赋值源的成员变量,其实也是函数级的成员变量。

    【摘要】

    类的继承以及对象的赋值会带来成员变量的相互传递。这里详细讨论了,类间继承带来的成员变量的传递采用覆盖原则,采用函数级的成员变量的取值;对象赋值带来的成员变量的传递采用,实函数采用数据类型的实函数,虚函数采用赋值源的虚函数,成员变量采用赋值源的成员变量,其实也是函数级的成员变量。

    【正文】

    在类继承中,成员变量存在覆盖的情况,成员函数则存在隐藏和覆盖以及重载的情况。在类继承中,公有继承会导致公有成员变量的覆盖,从而使得成员函数的调用出现各种结果。

    【代码示例 01】

    #include<iostream>
    using namespace std;
    class A
    {
    public:
        int m_a;
        A()
        {
            m_a = 1;
        }
        void print ()
        {
            printf("%d",m_a);
        }
    };
    class B : public A
    {
    public:
        int m_a;
        B()
        {
            m_a = 2;
        }
    };
    int main ()
    {
        B b;
        b.print();
        printf("%d\n",b.m_a);
    }

    解析:

    B 类中的 m_a 把 A 类中的 m_a 覆盖掉了。在构造 B 类时,先调用 A 类的构造函数。所以, A 类中的m_a 是1,b.print()打印的是 A 类中的m_a而B类中的m_a 是2。如果 B 类中存在函数 print,那么输出将是 B 的 print 函数以及 B 的成员变量。
    输出:
    12
    【小结】

    因为,调用了基类中的函数,所以,变量也是采用基类中的变量,而不是本类中的变量。


    【代码示例 02】

    #include <iostream>
    using namespace std;
    class A
    {
    protected:
    	int m_data;
    public:
    	A(int data = 0)
    	{
    		m_data = data;
    	}
    	int GetData()
    	{
    		return doGetData();
    	}
    	virtual int doGetData()
    	{
    		return m_data;
    	}
    };
    class B: public A
    {
    protected:
    	int m_data;
    public:
    	B(int data = 1)
    	{
    		m_data = data;
    	}
    	int doGetData()
    	{
    		return m_data;
    	}
    };
    class C: public B
    {
    protected:
    	int m_data;
    public:
    	C(int data = 2)
    	{
    		m_data = data;
    	}
    };
    int main()
    {
    	C c(10);
    	cout << c.GetData() << endl;     //1
    	cout << c.A::GetData() << endl;  //2
    	cout << c.B::GetData() << endl;  //3
    	cout << c.C::GetData() << endl;  //4
    	cout << c.doGetData() << endl;   //5
    	cout << c.A::doGetData() << endl;//6
    	cout << c.B::doGetData() << endl;//7
    	cout << c.C::doGetData() << endl;//8
    	system("PAUSE ");
    	return 0;
    }

    解析:

    @1. 本来是调用C类的GetData(),C中未定义,故调用B中的,但是B中也未定义,故调用A中的GetData(),因为A中的doGetData()是虚函数,所以调用B类的doGetData(),而B类的doGetData返回B::m_data,故输出1.
    @2. 因为A中的doGetData()是虚函数,又因为C类中未重定义该接口,所以调用B中的doGetData(),返回值同上。
    @3. 必须返回1
    @4. 同@1
    @5. 调用父类B中的doGetData(),输出1
    @6. 直接调用A中的doGetData(),所以输出0
    @7. 直接调用B中的doGetData(),所以输出1
    @8. 同@5
    【小结】

    这里的成员变量采用保护的访问控制类型,成员变量是不会被子类覆盖的。一般成员函数的调用顺序是,自己有就用自己的,自己没有,自底向上的向基类索取。虚函数的调用顺序是:自己有就用自己的,自己没有则自顶向下检索,到距离自己最近的派生类停止,其实也是向上索取的原则。


    在基类与派生类的对象或者对象指针互相转换、赋值时,会产生变量传递、一般成员函数传递和虚函数传递。这里只讨论因为对象或者对象指针的转换,造成成员变量的传递。

    【代码示例 03】

    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	A()
    	{
    		m_a = 1;
    		m_b = 2;
    	}
    	~A(){};
    	void fun()
    	{
    		printf("%d%d",m_a,m_b);
    	}
    private:
    	int m_a;
    	int m_b;
    };
    class B
    {
    public:
    	B()
    	{
    		m_c=3;
    	}
    	~B();
    	void fun()
    	{
    		printf("%d",m_c);
    	}
    private:
    	int m_c;
    };
    void main ()
    {
        A a;
        B *p = (B *)(&a);
        p->fun();
    }
    解析:

    首先可以肯定的是上面的代码是非常槽糕的,无论是可读性还是安全性都很差。写这种代码的人,按照Jarne Stroustrup(C++标志化制定者)的说法,应该“斩立决”。

    但是不得不说这也是一道很好考察你对内存偏移的理解的题:B *p = (B *)(&a); 。

    这是一个野蛮的转化,强制把 a 地址内容看成是一个B类对象,p 指向的是 a 类的内存空间。

    B类只有一个元素m_c 但是 A类的内存空间存放第一个元素的位置是 m_a, p指向的是对象的内存首地址,比如:0x22ff58,但p->fun()调用B::fun()来打印m_c时,编译器对m_c的认识就是m_c距离对象的偏移量是0,于是打印了对象A首地址的偏移量0x22ff58+0变量值,即就是m_a的值1 。


    【代码示例 04】

    #include<iostream>
    using namespace std;
    class A
    {
    public:
    <span style="white-space:pre">	</span>void virtual f()
    <span style="white-space:pre">	</span>{
    <span style="white-space:pre">		</span>cout << "A" << endl;
    <span style="white-space:pre">	</span>}
    <span style="white-space:pre">	</span>void rf1(int num = 1)
    <span style="white-space:pre">	</span>{
    <span style="white-space:pre">		</span>ma = num;
    <span style="white-space:pre">		</span>cout<<ma<<endl;
    <span style="white-space:pre">	</span>}
    private:
    <span style="white-space:pre">	</span>int ma;
    };
    class B : public A
    {
    public:
    <span style="white-space:pre">	</span>void virtual f()
    <span style="white-space:pre">	</span>{
    <span style="white-space:pre">		</span>cout << "B" << endl;
    <span style="white-space:pre">	</span>}
    <span style="white-space:pre">	</span>void rf2(int num = 2)
    <span style="white-space:pre">	</span>{
    <span style="white-space:pre">		</span>mb = num;
    <span style="white-space:pre">		</span>cout<<mb<<endl;
    <span style="white-space:pre">	</span>}
    private:
    <span style="white-space:pre">	</span>int mb;
    };
    int main()
    {
    <span style="white-space:pre">	</span>A* pa = new A();
    <span style="white-space:pre">	</span>pa->f();        //A 显然
    <span style="white-space:pre">	</span>pa->rf1();<span style="white-space:pre">		</span>//1
    <span style="white-space:pre">	</span>B* pb = (B*) pa;
    <span style="white-space:pre">	</span>pb->f();        //A  转化pa为B类型并新建一个指针pb,将pa复制到pb;pa的指向始终没有变化,所以pb也指向pa的f()函数,不存在覆盖问题。
    <span style="white-space:pre">	</span>pb->rf1();<span style="white-space:pre">		</span>//1
    <span style="white-space:pre">	</span>// pa->rf2();<span style="white-space:pre">		</span>//报错<span style="white-space:pre">	</span>
    <span style="white-space:pre">	</span>delete pa, pb;
    <span style="white-space:pre">	</span>pa = new B();
    <span style="white-space:pre">	</span>pa->f();        //B 显然
    <span style="white-space:pre">	</span>pa->rf1();<span style="white-space:pre">		</span>//1
    <span style="white-space:pre">	</span>// pa->rf2();<span style="white-space:pre">		</span>//报错
    <span style="white-space:pre">	</span>pb = (B*) pa;
    <span style="white-space:pre">	</span>pb->f();        //B 覆盖
    <span style="white-space:pre">	</span>pa->rf1();<span style="white-space:pre">		</span>//1
    <span style="white-space:pre">	</span>// pa->rf2();<span style="white-space:pre">		</span>//报错
    <span style="white-space:pre">	</span>return 0;
    }

    解析:

    实函数采用对象的数据类型,虚函数采用赋值源的数据类型,成员变量采用赋值源的数据类型的成员变量。如果对象被销毁,那么将采用对象新的指向数据类型,即为新的虚函数以及成员变量。

    【最后的话】

    指针的类型是实函数的类型,指针所指向对象的类型是虚函数的类型也是变量的值 !!!


    展开全文
  • //派生类的对象可以赋值给基类的对象,这时是把派生类对象从对应基类中继承来的隐藏对象赋值给基类对象。 //反过来不行,因为派生类的新成员无值可赋。 #include using namespace std; class B { public: B() ...
    //继承派生中对象相互赋值情况
    //派生类的对象可以赋值给基类的对象,这时是把派生类对象中从对应基类中继承来的隐藏对象赋值给基类对象。
    //反过来不行,因为派生类的新成员无值可赋。
    
    #include <iostream>
    using namespace std;
    class B
    {
    public:
    	B()
    	{
    		cout<<"B"<<endl;
    	}
    	void fun()
    	{
    		cout<<"B::fun()"<<endl;
    	}
    private:
    	int x;
    };
    
    class D : public B
    {
    public:
    	D()
    	{
    		cout<<"D"<<endl;
    	}
    	void fun()
    	{
    		cout<<"D::fun()"<<endl;
    	}
    	void show()
    	{
    		cout<<"D::show()"<<endl;
    	}
    private:
    	int y;
    };
    void main()
    {
    	B b;
    	b.fun();
    	D d;
    	d.fun();
    	b = d;    //正确,把大的给小的,给局部赋值
    	//d = b;    错误,小的给大的,不能赋值  
    }
    <img src="https://img-blog.csdn.net/20150512213119072?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZG91ZG91d2ExMjM0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
    
    

    展开全文
  • 赋值兼容规定:在继承允许向上赋值,不允许向下赋值: 假如有层次关系如下: 每个类里的具体内容(不是Java代码) 一般正常定义对象: Manager m = new Maneger(); 兼容规则可以让我们这么做: ...

    赋值兼容规定:在继承树中允许向上赋值,不允许向下赋值:

    假如有层次关系如下:

     

    每个类里的具体内容(不是Java代码) 

    一般正常定义对象:

    Manager m = new Maneger(); 

     兼容规则可以让我们这么做:

    Employee e = new Manager();
    

    为什么可以这样呢?

    当你定义的Employee e是用一个Manager()去实例化(ps:就是给e分配一个Manager()型的储存空间)的时候,你还是可以按照它是Employee类的方式去访问它的对象

    比如:

    e.Organizing_ability;

    Manager()类继承了Employee()类的Organizing_ablility,所以尽管用了Manager()类依然不影响这一点。

    同样的,你也可以:

    Object o = new Manager();

    或者:

    Object o = new Contractor();

    但如果反过来(向下赋值):

    Manager m = new Empolyee();

    当你想按照m是一个Manager()类的实例去访问他的内部成员时:

    m.Leadership;

    程序将会报错:

    因为之前向下赋值,给m分配了一块Employee()型的储存空间,而这里面根本没有Leadership这个属性。

    对象转换: 

    上面我们定义过:Employee e = new Manager();

    虽然可以这么定义,但实际上还是要转换为正确的类型才能方便实际应用。

    即:

    Manage m=(Manager)e;

    转换的时候,注意判断对象是不是别的子类的实例,这里会用到“instanceof”。

    public void MesEx(Employee e) {
        if(e instanceof Manager){
            Manager m = (Manager)e;
            System.out.println("Actually, e is a Manager");
        }
        else if(e instanceof Contractor){
            Contractor c = (Contractor)e;
            System.out.println("Actually, e is a Contractor");
        }
        else 
            System.out.println("e is really a Employee");
    }

    实参中的对象转换:

    public Salary CacuSy(Employee e) {
        //针对e内各个成员进行操作
    }

    写程序的时候,我们可以这样定义:

    Manager m = new Manager();
    Salary S = CacuSy(m);

    因为m是Manager()类(Employee类的子类),所以调用CacuSy()方法时,方法对e进行各种“Employee类成员”的操作时,在m里也能找到相应的成员。

    *异类集合:

    根据上面定义的几个类:

    我们可以定义一个异类集合去存放那些相关的父类对象,子类对象。

    Employee [] staff = new Employee[1024];
    staff[0] = new Manger();
    staff[1] = new Employee();
    staff[2] = new Contractor();

     

    展开全文
  • 问题在大犀牛那本书里的第125页,在讲解给对象属性赋值的时候有一句话说,“假设给对象o的属性x赋值,如果o不存在属性x,那么赋值操作给o添加一个新属性x。如果之前o继承自属性x,那么这个继承的属性就被新创建的...
  • 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由...
  • 派生类的对象赋值给基类对象

    千次阅读 2017-03-20 22:58:38
    对象赋值 Father fa; Son so; fa=so;//编译成功 so=fa;//失败 分析so=fa出错原因:赋值运算会调用operator =()函数,这个函数将运算符右边的对象成员赋值给运算符左边的对象,由于operator是左边的对象调用的...
  • 浅谈一下JAVA对象,对象引用以及对象赋值

    万次阅读 多人点赞 2013-09-19 00:50:29
    浅谈一下JAVA对象,对象引用以及对象赋值   今天有班级同学问起JAVA对象的引用是什么。正好趁着这次机会,自己总结一下JAVA对象,对象引用以及对象赋值。自己总结了所看到的网上相关方面的不少帖子,整理汇总形成...
  • java继承,子类构造方法赋值给父类对象,通过该对象访问方法和变量的不同情况说明
  • 如下图所示:当基类含有两个数据成员m_strName和m_iAge时,不管是公有私有还是保护类型的,都会被子类继承过来,同时子类应该还有他自身的数据成员,m_strCode和m_ISalary,当我们用子类的对象给基类的对象赋值或者...
  • 关于C++赋值运算符能不能继承问题

    千次阅读 2016-03-30 14:29:16
    C++的赋值运算符是可以被继承的。 有的人说不能被继承,“赋值运算符重载函数”不是不能被派生类继承,而是被派生类的默认“赋值运算符重载函数”给覆盖了。 条款45: 弄清C++在幕后为你所写、所调用的函数 一个空类...
  • java 子类对象赋值给父类对象的使用,包括代码及详解,个人笔记
  • Java父子对象属性赋值问题:现有父类Father类,子类Son类继承于Father类,现要求将父类对象的属性值全部赋予给子类,如果通过子类对象.set(父类对象.get属性),那势必相当麻烦。 那么,有没有更加方便的API呢?...
  • 继承赋值

    2013-11-20 18:21:32
    不能讲父类的对象赋值给子类对象 因为对象的赋值操作调用了一个函数 operator=()函数,该函数会将运算符右边的这个对象的成员赋给左边的对象  operator=()函数是左边对象调用的,所以赋值操作以左边对象为准
  • 继承机制(inheritance)是面向对象程序设计使代码可以复用的最重要手段。他允许程序员在保持原有类特性的基础上进行扩展,增加功能。继承是类之间的关系建模,共享公有的东西,实现各自本质不同的东西。这样产生...
  • 浅谈将子类对象赋值给父类对象

    万次阅读 多人点赞 2015-12-04 17:23:27
    最近对将子类对象赋值给父类对象有点心得,想和大家分享一下,但本人水平有限,请各位指正和批评。言归正传,下面是几个小例子,请大家看一看。测试一 父类:public class Supclass { public void print() { ...
  • 如果子类对象赋值给父类变量,则使用该变量只能访问子类的父类部分(因为子类含有父类的部分,所以不会有问题) 但是,如果反过来,这个子类变量如果去访问它的扩充成员变量,就会访问不到,因为原变量不包含该部分...
  • c++派生类对象赋值给基类对象

    千次阅读 2017-04-10 10:36:37
    基类对象和派生类对象之间的赋值关系具体是指:基类的对象可不可以赋值给子类对象或者子类对象可不可以赋值给基类对象。  一般来说,只有派生类的对象可以赋值给基类的对象,反之,则不可以。例如: ...
  • 文章目录1 C++继承中父类和子类之间的赋值兼容1.1 父子间的赋值兼容1.2 特殊的同名函数 1 C++继承中父类和子类之间的赋值兼容 1.1 父子间的赋值兼容 在公有继承的条件下,子类对象可以当作父类对象使用(兼容性): ...
  • 浅谈java对象引用及对象赋值

    千次阅读 多人点赞 2017-01-05 15:11:46
    一、Java对象及其引用  初学Java,总是会自觉或不自觉地把Java和C++相比较。在学习Java类与对象章节的时候,发现教科书和许多参考书把对象对象的引用混为一谈。可是,如果分不清对象对象引用, 那实在没法很...
  • 这些指针在运行时用于适当的函数调用,因为在编译时它可能还不知道是否要调用基函数或者是从继承基类的类实现的派生类。 虚拟方法表解决方案在C++及其相关语言(如D和C#)尤为常见。将对象的编程接口从实现分离出来...
  • 所以如果子类对象赋值给父类对象,那么这个对象只能访问子类的父类里面的内容。但是子类对父类做了扩充,也就是说,子类有父类没有的部分。所以如果父类对象赋值给子类对象,那么系统就会报错。 ...
  • 开发在中经常需要将一个对象的若干个值赋值给另外一个对象相对应的字段,且字段名是一样的,如果一个一个取一个一个赋值 太麻烦,使用org.springframework.beans.BeanUtils的copyProperties方法,有个弊端就是会将...
  • http://zhangyulong.iteye.com/blog/1462279 Java代码  ...问题如下    public static void main(String[] args){   User c1=new User();   c1.setId(1);     User c2=n
  • C++类继承下的赋值运算符

    千次阅读 2016-01-20 15:16:12
    将*this强制转化为Base&类型,调用基类的赋值运算符,只对基类Base部分进行赋值 ,注意这里必须转换成引用类型Base&,如果转成Base会调用拷贝构造函数创建新的对象,新对象成为赋值目标,而*this的成员保持不变,...
  • 在android 应用开发,经常是要处理服务接口返回的数据的,因此会将服务器数据(一般为json)转换为Model对象模型,但是有时候会有把一个model的属性值赋值给另一个model属性值的情况,例如一个模块或者方法只要求...
  • 理解对象赋值给接口

    千次阅读 2018-12-26 09:55:41
    = 实现该接口------那么就不可以将对象赋值给该接口 package main import "fmt" type Animal1 interface { say() } type Animal2 interface { color() } type felid interface { Animal1 Animal2 } type ...
  • 多重继承及虚继承中对象内存的分布 绝对经典----感觉以前了解的太肤浅了,中国大地人才真多呀!!!--原文字太小了,给放大了!嘿嘿!! 多重继承 首先我们先来考虑一个很简单(non-virtual)的多重继承。看看...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 356,183
精华内容 142,473
关键字:

关于继承中的对象赋值问题