精华内容
下载资源
问答
  • 缺点:父类指针,只能使用父类的成员 优点:提高复用性 要解决这个缺点, 多态 。 #include using namespace std; class CPerson { public: int a; public: void AA() { cout ()" ; } }; class ...

    缺点:父类指针,只能使用父类的成员
    优点:提高复用性

    要解决这个缺点,多态

    #include <iostream>
    using namespace std;
    
    class CPerson     
    {
    public:
    	int a;
    public:
    	void AA()
    	{
    		cout << "CPerson::CC()"  << endl;
    	}
    };
    
    
    class CSuperMan : public CPerson
    {
    public:
    	int b;
    public:
    	void BB()       
    	{
    		cout << "CSuperMan::CC()"  << endl;
    	}
    };
    
    
    
    
    int main()
    {
    	//CSuperMan* p = new CSuperMan;
    	//p->a = 100;
    	//p->b = 200;
    	//p->AA();
    	//p->BB();
    	//delete p;
    
    	CPerson* ps = new CSuperMan;    //  父类指针只能用父类的成员
    	ps->a = 100;
    	ps->AA();
    
    
    	system("pause");
    	return 0;
    }
    
    展开全文
  • 一、父类指针指向子类对象 先上代码: 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指针指向你,也就是将你强制转换成等号左边的类型。第一关就通不过,所以肯定不能这样操作。

    展开全文
  • c++父类指针指向子类对象

    千次阅读 2018-11-08 15:50:04
    c++父类指针指向子类对象   父类子类指针函数调用注意事项 1,如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数(静态联翩) 2,如果以一个衍生类指针指向一个基础...

    c++父类指针指向子类对象

     

    父类子类指针函数调用注意事项
    1,如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数(静态联翩)
    2,如果以一个衍生类指针指向一个基础类对象,必须先做强制转型动作(explicit cast),这种做法很危险,也不符合生活习惯,在程序设计上也会给程序员带来困扰。(一般不会这么去定义)
    3,如果基础类和衍生类定义了相同名称的成员函数,那么通过对象指针调用成员函数时,到底调用那个函数要根据指针的原型来确定,而不是根据指针实际指向的对象类型确定。

     


    虚拟函数就是为了对“如果你以一个基础类指针指向一个衍生类对象,那么通过该指针,你只能访问基础类定义的成员函数”这条规则反其道而行之的设计。
    如果你预期衍生类由可能重新定义一个成员函数,那么你就把它定义成虚拟函数( virtual )。
    polymorphism就是让处理基础类别对象的程序代码能够通透的继续适当地处理衍生类对象。
    纯虚拟函数:
    virtual void myfunc ( ) =0;
    纯虚拟函数不许定义其具体动作,它的存在只是为了在衍生类钟被重新定义。只要是拥有纯虚拟函数的类,就是抽象类,它们是不能够被实例化的(只能被继承)。如果一个继承类没有改写父类中的纯虚函数,那么他也是抽象类,也不能被实例化。
    抽象类不能被实例化,不过我们可以拥有指向抽象类的指针,以便于操纵各个衍生类。
    虚拟函数衍生下去仍然是虚拟函数,而且还可以省略掉关键字“virtual”。
    看个例子:

    #include <iostream> 
    using namespace std; 
    class A 
    { 
    public: 
        virtual void foo() 
        { 
            cout << "A's foo()" << endl; 
            bar(); 
        } 
        virtual void bar() 
        { 
            cout << "A's bar()" << endl; 
        } 
    }; 
    class B: public A 
    { 
    public: 
        void foo() 
        { 
            cout << "B's foo()" << endl; 
            A::foo(); 
        } 
        void bar() 
        { 
            cout << "B's bar()" << endl; 
        } 
    }; 
    int main() 
    { 
        B bobj; 
        A *aptr = &bobj; 
        aptr->foo(); 
        A aobj = *aptr; //转化为A类对象
        aobj.foo(); 
    }
    
    
    aptr->foo()输出结果是:
       B's foo()//这个明白,多态性
       A's foo()//这个也明白,执行A::foo();
       B's bar()//虽然调用的是这个函数:A::foo(); 但隐式传入的还是bobj 的地址,所以再次调用bar();调用时还是会调用B的函数, 与虚函数指针有关
    aobj.foo()输出结果是:
      A's foo() //这个不是指针,aobj完全是一个A的对象,与多态没有关系
      A's bar() 
    展开全文
  • 文章目录虚析构函数父类指针指向子类对象||子类指针指向父类对象总结: 虚析构函数 《Effective C++ 》 当派生类(derived class)对象由一个基类(base class)指针删除时,若基类有一个非虚函数(non-virtual)...

    虚析构函数

    《Effective C++ 》

    当派生类(derived class)对象由一个基类(base class)指针删除时,若基类有一个非虚函数(non-virtual)的析构函数时,其结果是未定义的——实际执行时通常发生的是对象的派生类部分没有被销毁。例如下面的示例:

    #include <iostream>
    using namespace std;
    
    class Shape
    {
    public:
      Shape() {
        cout <<"CRAT: shape" <<endl;
      }
    
      ~Shape() {
        cout <<"DEST: shape" <<endl;
      }
    };
    
    class Player
    {
    public:
      Player() {
        cout <<"CRAT: player" <<endl;
      }
    
      ~Player() {
        cout <<"DEST: player" <<endl;
      }
    };
    
    class Ball
    {
    public:
      Ball() {
        cout <<"CRAT: ball" <<endl;  
      }
    
      ~Ball() {
        cout <<"DEST: ball" <<endl; 
      }
    
    private:
      Shape shape_;
    };
    
    class Football : public Ball
    {
    public:
      Football() {
        cout <<"CRAT: football" <<endl;
      }
    
      ~Football() {
        cout <<"DEST: football" <<endl;
      }
    
    private:
      Player players_;
    };
    
    int main()
    {
      Ball *ball = new Football();
      delete ball;
      return 0;
    }
    

    上面的示例程序输出结果如下:

    CRAT: shape
    CRAT: ball
    CRAT: player
    CRAT: football
    DEST: ball
    DEST: shape
    

    可以看到,当基类指针指向派生类对象时,在删除对象时,并没有调用派生类成员对象及派生类自身的析构函数,而只是调用了基类成员对象及基类的析构函数,于是就造成了“局部销毁”对象的现象,从而导致内存泄露,正确的做法是为基类指定一个虚析构函数 。

    为了避免上述问题的出现,我们是不是可以为每个类都声明一个虚析构函数呢?考虑如下的示例:

    class Point
    {
    public:
        Point(int x, int y);
        ~Point();
    
    private:
        int x_, y_;
    };
    

    上述Point类在32-bit机器上所占用的内存空间为8字节。若我们将Point类的析构函数指定为虚函数,那么Point类不得不提供一个vptr(即,virtual table pointer)指针,它指向一个由函数指针构成的数组(vtbl, virtal table)。每个带有虚函数的类都有一个相应的vtbl。当对象调用某个虚函数时,实际被调用的函数取决于该对象的vptr所指向的那个vtbl。因此,无端的将所有类的析构函数声明为虚函数也是错误的。

    父类指针指向子类对象||子类指针指向父类对象

    1.当自己的类指针指向自己类的对象时,无论调用的是虚函数还是实函数,其调用的都是自己的:

    2.当指向父类对象的父类指针被强制转换成子类指针时候,子类指针调用函数时,只有非重写函数是自己的,虚函数是父类的;

    3.当指向子类对象的子类指针被强制转换成父类指针的时候,也就是父类指针指向子类对象,此时,父类指针调用的虚函数都是子类的,而非虚函数都是自己的;

    将上面三句话总结成一句话就是:当父类子类有同名非虚函数的时候,调用的是转换后的指针类型的函数;

    当父类子类有同名虚函数的时候呢,调用的是指针转换前指向的对象类型的函数。
    当基类指针指向派生类的时候,只能操作派生类从基类中继承过来的数据。
    指向派生类的指针,因为内存空间比基类长,访问的话会导致内存溢出,所以不允许派生类的指针指向基类。

    1.每个析构函数(不加 virtual) 只负责清除自己的成员。
    2.可能有基类指针,指向的确是派生类成员的情况。(这是很正常的),
    那么当析构一个指向派生类成员的基类指针时,程序就不知道怎么办了。
    所以要保证运行适当的析构函数,基类中的析构函数必须为虚析构。
    基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。所以,将析构函数声明为虚函数是十分必要的。
    只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。

    理解:

    通常来说,子类总是含有一些父类没有的成员变量,或者方法函数。而子类肯定含有父类所有的成员变量和方法函数。所以用父类指针指向子类时,没有问题,因为父类有的,子类都有,不会出现非法访问问题。
    但是如果用子类指针指向父类的话,一旦访问子类特有的方法函数或者成员变量,就会出现非法,因为被子类指针指向的由父类创建的对象,根本没有要访问的那些内容,那些是子类特有的,只有用子类初始化对象时才会有。

    总结:

    带多态性质的基类应该声明一个虚析构函数,如果类中包含其他虚函数,也应该拥有一个虚析构函数。
    若类的设计目的不是作为基类使用,或不是为了具备多态性,就不应该声明虚析构函数。

    展开全文
  • 父类指针指向子类实例对象,对于普通重写函数时,会调用父类中的函数。而调用被子类重写虚函数时,会调用子类中的函数。 这是因为子类中被重写的虚函数的运行方式是动态绑定的,与当前指向类实例的父类指针类型无关...
  • C++父类指针指向子类对象时,父类析构必须为虚函数 示例1 [1]: #include &amp;amp;lt;iostream&amp;amp;gt; using namespace std; class Base_J { public: Base_J() { cout &amp;...
  • 一篇文章讲通: ①C++里面的父类指针指向子类对象[与C++多态的知识点有关] 与 ②java里面的父类引用(或者说父类变量)指向子类对象[与java多态的知识点有关] [在java中,这个简称:上转型]
  • 即:没有虚函数的继承:如果一个基础类(父类)指针指向派生类(子类),那么该指针只能调用基础类(父类)定义的函数(如果子类重写了函数,那么因为定义的是父类指针掉的将会是父类的函数,即根据指针的原型来确定...
  • 子类父类指针调用相关注意事项 1,如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数(静态联翩) 2,如果以一个衍生类指针指向一个基础类对象,必须先做强制转型动作...
  • 父类指针可以指向子类对象,但是只能使用父类的一些方法变量, 子类特有的不能使用,除非数据强转 如: static_cast(subject)->NewData(); 或者: ((ConcreteSubject*)subject)->NewData(); 进行数据类型强转 ...
  • 1,如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数(静态联翩) 2,如果以一个衍生类指针指向一个基础类对象,必须先做强制转型动作(explicit cast),这种做法很...
  • 1 前言 记得当初阿里面试的时候被问道这个问题,平时自己面对这个方法都习以为常的使用,C++多态和动态绑定不就是这么实现的嘛,但是还真没有刻意... 如果以一个派生类类指针指向一个基础类对象,必须先做强制转型动作
  • 1,如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数(静态联翩) 2,如果以一个衍生类指针指向一个基础类对象,必须先做强制转型动作(explicit cast),这种做法...
  • 父类子类指针函数调用注意事项1,如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数(静态联翩)2,如果以一个衍生类指针指向一个基础类对象,必须先做强制转型动作...
  • 1,如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数(静态联翩) 2,如果以一个衍生类指针指向一个基础类对象,必须先做强制转型动作(explicit cast),这种做法很...
  • 简而言之就是用父类型的指针指向子类的实例,然后通过父类的指针调用实际子类的成员函数。但仅仅可调用父类含有的函数,非父类函数不能调用。 普通虚函数调用 假设我们有下面的类层次: #include &lt;...
  • 假设基类(父类)是Base,派生类(子类)是Derived。 之前用到在网上查都说用 Derived d; Base *bp = &amp;d; Derived *dp = (Base*)bp; 但实际上这是错误的,这样得到的派生类指针是不能指向派生类的成员...
  • class A { public: A() { printf("A \n"); } ~A() { printf(" ~A \n"); }// 这里不管写不写...因为这个例子是B*指针指向B对象,不是A*指针指向B对象。 }; class B : public A { public: B() { printf("B \n"); .
  • 参考链接

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 73,889
精华内容 29,555
关键字:

父类指针指向子类对象