开发者

c++ redefining types in subclasses and slicing

开发者 https://www.devze.com 2023-02-18 13:45 出处:网络
#include <string> #include <iostream> class a { public: int x;}; class b : public a{public: int x; } ;
#include <string>
#include <iostream>

class a { public: int x;};
class b : public a  {public: int x; } ;

int main()
{
    b bee;
    bee.x = 3;

    a ay = bee;
    std::cout << std::endl << ay.x << std::endl;


}

The code above compiles fine in clang 3.0 and g++ 4.5. However the output is junk (--i.e., not three). Since the compiler doesn't seem to mind, how do I get the code to behave ?

Secondly, If there is some way to make the above slice / conversion to work correctly, how bad would it 开发者_运维知识库be if I then did the following, provided a good reason to do it exists :

class c : public a { public: uint64_t x; };

Why I am interested in these semantics.

The reason I want to do this is this. I have a two class heirachies, where one heirarchy (the parent) aggregages objects, on the same heirarchy level, from the other(the child). I use a custom container for the aggregation. I want to typedef the container in the parent class (the typedefs have the same name), and declare the container with the same name at each level of the parent.

The class heirarchies are designed to contain less information at lower levels ( the base classes hold the least), therefore slicing makes perfect sense here.

Edit:

There you go, this should clear things up.

class A { int x; }; 
class B : public A {int y;};
class Ap {std::vector<A> entries;};
class Bp : Ap{std::vector<B> entries;};

The child B has more members than the child class A. However, I wan't to present a uniform interface for code that is only interested in the members of class A.


There is no way to do that if you directly set b::x. a::x and b::x are two different members, and the latter hides the former.

You can still access a::x on an object of type b with static_cast<a&>(bee).x = 3, but the fundamental problem is that the values of a::x and b::x on an object of type b are not synchronized.

If you abstract access to both x members with a "property getter/setter", then you can arrange for the setter on the derived class to also update the member of the base class. Or (maybe this is more appropriate?) you can make the member of the base class protected and use it from the derived class directly, slicing as you need just before returning from the getter.


huh ! its a bit complicated no ?

why don't you use :

class a
{
virtual void set( int value ) { x = value; }

protected :
    int x;
};

class b : public a 
{
virtual void setA( int value ) { a::x = value; }

or

virtual void setA( int value ) { b::x = value; }

or

virtual void setA( int value ) { a::x = value; b::x = value; }

protected:
    int x;
} ;

There are two ways of constructing a software design; one way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult. C.A.R.Hoare


According to Jon's answer, Since a::x and b::x are separate variables, furthermore since b::x masks a::x, if you wanted to get the correct semantics you need to provide a copy conversion constructor. The following code does the trick.

#include <string>
#include <iostream>

class b;

class a { 
public: 
    a();
    a(const b & bee);

    int x;
};
class b : public a  {public: int x; } ;


a::a() {}
a::a(const b & bee)
{ 
        x = bee.x;
}  

int main()
{
    b bee;
    bee.x = 3;

    a ay = bee;
    std::cout << std::endl << ay.x << std::endl; 
}


Maybe try something like this:

class A { int x; }; 
class B : public A {int y;};
class Ap {
public:
    void Append(A *pa)
    {
        entries.push_back(pa);
    }
    A *GetA(size_t nIndex) 
    {
        return entries.at(nIndex);
    }
private:
    std::vector<*A> entries;
};
class Bp : Ap
{ 
public:
    B *GetB(size_t nIndex) 
    {
        return dynamic_cast<B*>(GetA(nIndex));
    }
};
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号