精华内容
下载资源
问答
  • 多继承二义性

    千次阅读 2018-10-08 09:45:22
    多继承二义性主要分为两种: 调用不同基类的同名成员时可能出现二义性 访问共同基类的成员可能出现二义性 1、调用不同基类的同名成员时可能出现二义性 class A { public: void setA(int a); int get(); .....

    转自:https://www.cnblogs.com/tenjl-exv/p/7625484.html

    多继承的二义性主要分为两种:

    1. 调用不同基类的同名成员时可能出现二义性
    2. 访问共同基类的成员可能出现二义性

    1、调用不同基类的同名成员时可能出现二义性

    class A
    {
        public:
            void setA(int a);
            int get();
        private:
            int a;
    } ;
    class B
    {
        public:
            void setB(int b);
            int get();
        private:
            int b;
    } ;
    
    class C:public A,public B
    {
        public:
            void setC(int c);
            int getC();
        private:
            int c;
    };

     在执行obj.get();时将是有二义性的。因为类C分别从类A类B继承了两个不同版本的get()成员函数,因此,obj.get();到底调用哪个get()版本,编译器将无从知晓。

    有两种解决方法:

    (1)使用作用域分辨符::加以消除。

    obj.A::get();

    obj.B::get();

    (2)在类C中也定义成员函数get()函数,则有类C的对象obj访问get()函数obj.get()没有二义性,这是因为当派生类中的成员与基类中的成员重名时,派生类中的同名成员将被调用。

    class A
    {
        public:
            void setA(int a);
            int get();
        private:
            int a;
    } ;
    class B
    {
        public:
            void setB(int b);
            int get();
        private:
            int b;
    } ;
    
    class C:public A,public B
    {
        public:
            void setC(int c);
            int get();
                    //此处改为这样    
        private:
            int c;
    };

     2、访问共同基类的成员可能出现二义性

    菱形继承问题

    class A
    {
        public:
            void disp(); 
        private:
            int a;
    };
    
    class B1:public A
    {
        public:
            void dispB1();
        private: 
            int b1;
    };
    class B2:public A
    {
        public:
            void dispB2();
        private: 
            int b2;
    };
    
    class C:public B1,public B2
    {
        public:
            void dispC();
        private: 
            int c;
    };

     

    在此类结构下,如果创建类C的对象c1:

    C c1;

    则下面的两个访问都有二义性:

    c1.disp();

    c1.A::disp();

    不过下面的两条调用语句却是正确的:

    c1.B1::disp();

    c1.B2::disp();

    解决方法:

    采用虚基类的方式,代码如下:

    class A
    {
        public:
            void disp(); 
        private:
            int a;
    };
    
    class B1:virtual public A
    {
        public:
            void dispB1();
        private: 
            int b1;
    };
    class B2:virtual public A
    {
        public:
            void dispB2();
        private: 
            int b2;
    };
    
    class C:public B1,public B2
    {
        public:
            void dispC();
        private: 
            int c;
    };

     

    展开全文
  • 多继承二义性和虚继承(虚基类)

    千次阅读 2017-09-04 16:22:29
    但是,由于在多继承的情况下,可能出现基类中某个成员的访问不唯一的情况,这称为对基类成员访问的二义性。在多继承的情况下,通常有两种可能出现的二义性。 1 派生类的多个基类中调用其同名成员时可能出现二义性...

    一般来说,在派生类中对基类成员的访问是应该是唯一的。但是,由于在多继承的情况下,可能出现基类中某个成员的访问不唯一的情况,这称为对基类成员访问的二义性。

    在多继承的情况下,通常有两种可能出现的二义性。
    1 派生类的多个基类中调用其同名成员时可能出现二义性

    class A
    {
    public:
        void f(){}
    };
    
    class B
    {
    public:
        void f() {}
        void g() {}
    };
    
    class C : public A, public B
    {
    public:
        void h() {}
    };

    如果定义一个类C的对象c, 则对函数f()的访问c.f()就会出现二义性,是访问A中的f,还是B中的f。
    可以通过成员名限定发来消除二义性。例如:c.A::f() 或者 c.B::f()。

    2 派生类有共同基类时访问公共基类成员可能出现二义性

    class A
    {
    public:
        int a;
    };
    
    class B1 : public A
    {
    public:
        int b1;
    };
    
    class B2 : public A
    {
    public:
        int b2;
    };
    
    class C : public B1, public B2
    {
    public:
        int c;
    };

    如果定义了一个类C的对象c,则c.a 或者 c.A::a 都有问题。
    正确的访问为c.B1::a 或者 c.B2::a。

    对于第2中二义性,基类A被保存了2份。对C进行初始化时,类A会被初始化两次。 为了彻底避免在这种结构中的二义性,在创建派生类对象时对公共基类只进行一次初始化,则引入了虚继承虚基类。

    声明和定义:
    虚继承的引入是为了解决二义性问题。其说明格式如下:
    class <类名> : virtual <继承方式> <基类名>
    其中,virtual是虚继承的关键字。
    以虚继承方式被继承的基类称为虚基类。
    经过这样的声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次。

    需要注意: 为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。

    例如:

    class A
    {
    public:
        int a;
    };
    
    class B1 : virtual public A
    {
    public:
        int b1;
    };
    
    class B2 : virtual public A
    {
    public:
        int b2;
    };
    
    class C : public B1, public B2
    {
    public:
        int c;
    };

    如果定义了类C的对象c,则c.a是正确的。并且,类C中只有一份类A的内容。

    含有虚基类的子类的内存结构:
    还以上面的代码为例,类C是如何实现,只包含一份类A的呢?在引入虚基类之后,虚基类的直接子类中就包含了一个指向虚基类的指针,这个指针称为虚基类指针。类B1和B2中各有一个指向类A的虚基类指针,类C中只继承了一份类A,但继承了类B1和B2中的指向类A的指针。

    含虚基类的派生类的构造函数
    为了初始化基类的子对象,派生类的构造函数要调用基类的构造函数。对于虚基类,由于派生类的对象中只有一个虚基类的子对象。为了保证虚基类子对象只被调用一次,这个虚基类构造函数必须只被调用一次。由于继承机构可能很深,规定将在建立对象时指定的类称为最派生类。C++语言规定,虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。 而该派生类的直接基类中所列出的对这个虚基类的构造函数的调用在执行时被忽略,这样保证了对虚基类的子对象只初始化一次。

    C++语言规定,在一个成员初始化列表中,对虚基类的初始化先于非虚基类的初始化。

    class A
    {
    public:
        A(int a) : a(a) {}
        int a;
    };
    
    class B1 :  virtual public A
    {
    public:
        B1(int a,int b) : A(a), b1(b) {}
        int b1;
    };
    
    class B2 :  virtual public A
    {
    public:
        B2(int a, int b) : A(a), b2(b) {}
        int b2;
    };
    
    class C : public B1, public B2
    {
    public:
        C(int a, int b1,int b2, int c) : A(a), B1(b1,b1), B2(b2,b2), c(c) {}
        int c;
    };
    
    int main()
    {
        B1 b1(2,2);
        cout<<b1.a<<endl;
    
        B2 b2(3,3);
        cout<<b2.a<<endl;
    
        C c(1,2,3,4);
        cout<<c.a<<endl;
    }

    结果: 2 3 1
    在这段代码中,在类C中直接对类A进行初始化。并且,虽然类B1和类B2中都有对类A的初始化,但在对类C进行初始化时,B1和B2中对类A的初始化都被忽略了。这样就保证了类A只被初始化一次。

    展开全文
  • C++多继承中的二义性问题

    千次阅读 多人点赞 2018-05-24 17:35:46
    在C++中,派生类继承基类,对基类成员的访问应该是确定的、唯一的,但是常常...当派生类从个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生另一种不确定性——路径二义性。 1. ...
        在C++中,派生类继承基类,对基类成员的访问应该是确定的、唯一的,但是常常会有以下情况导致访问不一致,产生二义性。
        1.在继承时,基类之间、或基类与派生类之间发生成员同名时,将出现对成员访问的不确定性——同名二义性。
        2.当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生另一种不确定性——路径二义性。

        1.

                                                                        

    “倒三角”问题——同名二义性

    #include "iostream"
    
    using namespace std;
    
    class Parent_f
    {
    public:
        void show()
        {
            cout<<"This is Parent_f\n";
        }
    };
    class Parent_m
    {
    public:
        void show()
        {
            cout<<"This is Parent_m\n";
        }
    };
    class Son:public Parent_f,public Parent_m
    {
    public:
        void display()
        {
            cout<<"This is son\n";
        }
    };
    int main()
    {
        Son son;
        son.show();
        son.display();
        cout << "Hello world!" << endl;
        return 0;
    }

    上面的代码中,2个父类派生一个子类,但两个父类中都有同名的成员函数。派生出的子类产生二义性问题,编译时会报:

    error: request for member 'show' is ambiguous

        解决方法:

        (1)利用作用域限定符(::),用来限定子类调用的是哪个父类的show()函数

    son.Parent_f::show();
        (2)在类中定义同名成员,覆盖掉父类中的相关成员
    class Son:public Parent_f,public Parent_m
    {
    public:
        void display()
        {
            cout<<"This is son\n";
        }
    
        void show()
        {
            cout<<"show:This is son.\n";
        }
    };


    2.

                                                        

    “菱形”问题——路径二义性

        有最基类A,有A的派生类B、C,又有D同时继承B、C,那么若A中有成员a,那么在派生类B,C中就存在a,又D继承了B,C,那么D中便同时存在B继承A的a和C继承A的a,那么当D的实例调用a的时候就不知道该调用B的a还是C的a,就导致了二义性。

    #include "iostream"
    
    using namespace std;
    
    class Grandpa
    {
    public:
        int year_old;
    };
    
    class Parent_f:public Grandpa {};
    class Parent_m:public Grandpa {};
    
    class Son:public Parent_f,public Parent_m {};
    
    int main()
    {
        Son son;
        son.year_old = 10;
        cout << "Hello world!" << endl;
        return 0;
    }
    

        Grandpa为Parent_f和Parent_m的基类,而Son又继承Parent_f和Parent_m,当Son访问Grandpa的year_old时,会出现二义性错误.

        解决方法:

        (1)使用作用域限定符,指明访问的是哪一个基类的成员。

                注意:不能是Grandpa作用域下限定,因为Son直接基类的基类才是Grandpa,纵使指明了访问Grandpa的成员的话,对于Son来说,还是模糊的,还是具有二义性。

        (2)在类中定义同名成员,覆盖掉父类中的相关成员。

        (3)虚继承、使用虚基类

            教科书上面对C++虚基类的描述玄而又玄,名曰“共享继承”,名曰“各派生类的对象共享基类的的一个拷贝”,其实说白了就是解决多重多级继承造成的二义性问题。父类对祖父类的继承方式改为虚继承,那么子类访问自己从祖父类那里继承过来的成员就不会有二义性问题了,也就是将子类对象里的祖父类对象统一为一个,继承的只有一个祖父类对象,代码如下。

    #include "iostream"
    
    using namespace std;
    
    class Grandpa
    {
    public:
        int year_old;
        void show()
        {
            cout << "year_old:" << year_old <<endl;
        }
    };
    
    class Parent_f:virtual public Grandpa {};
    class Parent_m:virtual public Grandpa {};
    
    class Son:public Parent_f,public Parent_m {};
    
    int main()
    {
        Grandpa grp;
        Parent_f pa_f;
        Parent_m pa_m;
        Son son;
    
        grp.year_old = 100;
        pa_f.year_old = 55;
        pa_m.year_old = 50;
        son.year_old = 10;
    
        grp.show();
        pa_f.show();
        pa_m.show();
        son.show();
        cout << "Hello world!" << endl;
        return 0;
    }

        使用了虚基类的方法,是不是感觉简单方便多了呢








    展开全文
  • C++多继承二义性

    2013-05-18 15:24:35
    多继承可以看作是单继承的扩展。所谓多继承是指派生类具有多个基类,派生类与每个基类之间的关系仍可看作是一个单继承。  多继承下派生类的定义格式如下:  class :,,…  {    };  其中,,,...

    多继承可以看作是单继承的扩展。所谓多继承是指派生类具有多个基类,派生类与每个基类之间的关系仍可看作是一个单继承。

        多继承下派生类的定义格式如下:

        class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…

        {

        <派生类类体>

        };

        其中,<继承方式1>,<继承方式2>,…是三种继承方式:public、private、protected之一。例如:

        class A

        {

        …

        };

        class B

        {

        …

        };

        class C : public A, public B

        {

        …

        };

        其中,派生类C具有两个基类(类A和类B),因此,类C是多继承的。按照继承的规定,派生类C的成员包含了基类A, B中成员以及该类本身的成员。

        多继承的构造函数

        在多继承的情况下,派生类的构造函数格式如下:

        <派生类名>(<总参数表>):<基类名1>(<参数表1>),<基类名2>(<参数表2>),…

        <子对象名>(<参数表n+1>),…

        {

        <派生类构造函数体>

        }

        其中,<总参数表>中各个参数包含了其后的各个分参数表。

        多继承下派生类的构造函数与单继承下派生类构造函数相似,它必须同时负责该派生类所有基类构造函数的调用。同时,派生类的参数个数必须包含完成所有基类初始化所需的参数个数。

        派生类构造函数执行顺序是先执行所属基类的构造函数,再执行派生类本身构造函数,处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的各基类顺序,与派生类构造函数中所定义的成员初始化列表的各项顺序无关。也就是说,执行基类构造函数的顺序取决于定义派生类时基类的顺序。可见,派生类构造函数的成员初始化列表中各项顺序可以任意地排列。

        下面通过一个例子来说明派生类构造函数的构成及其执行顺序。

        #include <iostream.h>

        class B1

        {

        public:

        B1(int i)

        {

        b1 = i;

        cout<<"构造函数 B1."<<i<< endl;

        }

        void print()

        {

        cout<<"B1.print()"<<b1<<endl;

        }

        private:

        int b1;

        };

        class B2

        {

        public:

        B2(int i)

        {

        b2 = i;

        cout<<"构造函数 B2."<<i<< endl;

        }

        void print()

        {

        cout<<"B2.print()"<<b2<<endl;

        }

        private:

        int b2;

        };

        class B3

        {

        public:

        B3(int i)

        {

        b3 = i;

        cout<<"构造函数 B3."<<i<<endl;

        }

        int getb3()

        {

        return b3;

        }

        private:

        int b3;

        };

        class A : public B2, public B1

        {

        public:

        A(int i, int j, int k, int l):B1(i), B2(j), bb(k)

        {

        a = l;

        cout<<"构造函数 A."<<a<<endl;

        }

        void print()

        {

        B1::print();

        B2::print();

        cout<<"A.print()"<<a<<","<<bb.getb3()<<endl;

        }

        private:

        int a;

        B3 bb;

        };

        void main()

        {

        A aa(1, 2, 3, 4);

        aa.print();

        }

        该程序的输出结果为:

        构造函数 B2.2

        构造函数 B1.1

        构造函数 B3.3

        构造函数 A.4

        B1.print().1

        B2.print()2

        A.print()4, 3

        在该程序中,作用域运算符::用于解决作用域冲突的问题。在派生类A中的print()函数的定义中,使用了B1::print;和B2::print();语句分别指明调用哪一个类中的print()函数,这种用法应该学会。

    二义性问题

        一般说来,在派生类中对基类成员的访问应该是唯一的,但是,由于多继承情况下,可能造成对基类中某成员的访问出现了不唯一的情况,则称为对基类成员访问的二义性问题。

        实际上,在上例已经出现过这一问题,回忆一下上例中,派生类A的两基类B1和B2中都有一个成员函数print()。如果在派生类中访问 print()函数,到底是哪一个基类的呢?于是出现了二义性。但是在上例中解决了这个问题,其办法是通过作用域运算符::进行了限定。如果不加以限定,则会出现二义性问题。

        下面再举一个简单的例子,对二义性问题进行深入讨论。例如:

        class A

        {

        public:

        void f();

        };

        class B

        {

        public:

        void f();

        void g();

        };

        class C : public A, public B

        {

        public:

        void g();

        void h();

        };

        如果定义一个类C的对象c1:

        C c1;

        则对函数f()的访问

        c1.f();

        便具有二义性:是访问类A中的f(),还是访问类B中的f()呢?

        解决的方法可用前面用过的成员名限定法来消除二义性,例如:

        c1.A::f();

        或者

        c1.B::f();

        但是,最好的解决办法是在类C中定义一个同名成员f(),类C中的f()再根据需要来决定调用A::f(),还是B::f(),还是两者皆有,这样,c1.f()将调用C::f()。

        同样地,类C中成员函数调用f()也会出现二义性问题。例如:

        viod C::h()

        {

        f();

        }

        这里有二义性问题,该函数应修改为:

        void C::h()

        {

        A::f();

        }

        或者

        void C::h()

        {

        B::f();

        }

        或者

        void C::f()

        {

        A::f();

        B::f();

        }

        另外,在前例中,类B中有一个成员函数g(),类C中也有一个成员函数g()。这时,

        c1.g();

        不存在二义性,它是指C::g(),而不是指B::g()。因为这两个g()函数,一个出现在基类B,一个出现在派生类C,规定派生类的成员将支配基类中的同名成员。因此,上例中类C中的g()支配类B中的g(),不存在二义性,可选择支配者的那个名字。

        当一个派生类从多个基类派生类,而这些基类又有一个共同的基类,则对该基类中说明的成员进行访问时,也可能会出现二义性。例如:

        class A

        {

        public:

        int a;

        };

        class B1 : public A

        {

        private:

        int b1;

        };

        class B2 : public A

        {

        private:

        int b2;

        };

        class C : public B1, public B2

        {

        public:

        int f();

        private:

        int c;

        };

        已知:C c1;

        下面的两个访问都有二义性:

        c1.a;

        c1.A::a;

        而下面的两个访问是正确的:

        c1.B1::a;

        c1.B2::a;

        类C的成员函数f()用如下定义可以消除二义性:

        int C::f()

        {

        retrun B1::a + B2::a;

        }

        由于二义性的原因,一个类不可以从同一个类中直接继承一次以上,例如:

        class A : public B, public B

        {

        …

        }

        这是错误的。


    展开全文
  • 多继承二义性 二义性的产生:1.重名定义 2. 多路径继承 解决二义性的方法: 不重名 — 利用成员名限定法(Bird与Horse中的fun 与 m_weight不重命) 在派生类中定义一个同名成员;(在FlyHorse中也定义fun ...
  • 一般来说,在派生类中对基类成员的访问应该是唯一的,但是,由于在多继承情况下,可能出现对基类中某个成员的访问不唯一性情况,这称为对基类成员访问的多继承二义性问题 分两种情况: first: 派生类的多个基类中...
  • c++多继承二义性的另类解决办法

    千次阅读 2010-05-11 12:11:00
    多继承可以看作是单继承的扩展。所谓多继承是指派生类具有多个基类,派生类与每个基类之间的关系仍可看作是一个单继承。 多继承下派生类的定义格式如下: class :,,… { }; 其中,,,…是三种继承方式:public、...
  • 多重继承二义性问题

    千次阅读 2010-12-17 15:10:00
    C++ 多重继承 二义性
  • C++:多继承中的二义性问题

    千次阅读 热门讨论 2014-02-28 20:30:03
    但是,在多继承情况下,可能造成对基类中某个成员的访问出现了不一致的 情况,这时就称对基类成员的访问产生了二义性. 原因之一:  派生类在访问基类成员函数时,由于基类存在同名的成员函数,导致无法确定访问的是...
  • 多继承中二义性的问题

    千次阅读 2012-04-08 11:18:43
    假定通过个派生路径继承名为x的成员,有下面三种可能性: 1、如果每个路径中x表示同一虚基类成员,则没有二义性,因为共享该成员的单个实例。 2、如果在某个路径中x...像非虚多重继承层次一样,这种二义性最好用在
  • C++多继承中二义性的解决方案

    千次阅读 2018-05-27 15:30:24
    出现二义性的原因:派生类在访问基类成员函数时,由于基类存在同名的成员函数,...1. 什么是多重继承二义性 class A{ public: void f(); } class B{ public: void f(); void g(); } class C:public A,pub...
  • C++ 继承中的二义性

    千次阅读 2015-02-05 08:53:39
    多重继承的派生类有可能从两个或个基类继承同名成员,对该成员如果不加限定的话,这样使用就是二义性的。即使两个继承的函数有不同的形参表也会产生错误,类似的,即使函数在一个类中是私有的而在另一个类中是公用...
  • 1.赋值兼容规则 (1)派生类对象可以给基类对象赋值,这种情况下派生类对象将从基类继承的成员的值赋值给一个基类对象;但是不允许将一个基类的对象赋值给一个派生类。...2.多继承 (1)一个派生类从两个以上的基类中
  • //虚继承的意义//在多继承中,保存共同基类的多份同名成员,虽然有时是必要的,可以在不同的数//据成员中分别存放不同的数据,但在大多数情况下,是我们不希望出现的。因为保留多//份数据成员的拷贝,不仅占有较多的...
  • 菱形继承问题和虚继承是如何解决二义性与数据冗余的继承是c++的三大特性之一,其中菱形继承问题是一个值得我们学习和掌握的知识点。 1.什么是菱形继承呢? 菱形继承定义为:两个子类继承同一个父类,而又有子类...
  • 多重继承转换二义性

    2010-08-31 18:43:00
    如果一个类继承多个基类,而且这些基础继承相同的基类,则进行类型转换时,如果转换成相同的父类型,编译时会产生二义性错误:    class A  {   virtual void test(){} ;  } ;...
  • 多重继承二义性以及解决方法

    千次阅读 2013-02-27 09:31:29
    //多重继承二义性以及解决方法 //学习目的:了解类的继承原理及多重继承二义性的解决方法。 /* //本程序代码来源《MFC权威剖析》p68 */ ////////////////////第一种多重继承二义性//////////////// class ...
  • C++多重继承二义性避免

    千次阅读 2016-05-14 17:34:24
    1. 什么是多重继承二义性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class A{ public:  void f(); }   class B{ public:  void f();  void g(); }   ...
  • 继承中的二义性问题

    2019-03-15 09:42:33
    一、调用不同基类的同名成员时可能出现二义性 class A { public: void setA(int a); int get(); private: int a; } ; class B { public: void setB(int b); int get(); private:...
  • 一、派生类构造函数的写法(1)冒号...(2)需要注意的是:冒号后面是对基类构造函数的调用,而不是声明,所以括号里的参数是实参、基类构造函数调用规则(1)通过派生类创建对象时必须要调用基类的构造函数,这是
  • C++菱形继承如下: #include<iostream> using namespace std; class A { public: int a; }; class B1 : public A { public: int _B1; }; class B2 :public A { public: int _B2; }; class C :public B1, ...
  • 例如,当在派生类继承个基类中有同名成员时,派生类中就会出现标识不唯一(二义性)的情况,这在程序中是不允许的。如: #include using namespace std; class Base1 { public: int x; int a()
  • 它的出现,是为了克服继承中一个非常棘手的问题,也就是臭名昭著的菱形继承二义性)问题。 二义性,也就是说,假如我们有一个基类: class Dog //狗,虚基类 { public:  int getWeight() {  return m_...
  • 多重继承中二义性的消除

    千次阅读 2014-08-16 18:11:42
    类A派生B和C, 类D从B,C派生,如何将...这道题实际上考查的是如何消除多重继承引起的向上继承二义性问题。程序代码如下所示: class A {}; class B : public A {}; class C : public A {}; class D : public
  •  ...多重继承:一个类派生出个类,个类派生出一个类 性质与规则: 1.声明方法:class D:public A,private B,protected C{} 2.调用顺序:先调用A的构造函数,再到B,再到C。虚基类->非虚基
  • 在c++继承时可有个基类,所以可能出现二义性。可以用virtual来避免 /* 用virtual来避免继承类的二义性 可以把virtual去掉试试看 */ #include using namespace std; class Base { public:

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 48,306
精华内容 19,322
关键字:

关于多继承二义性