Virtual method from derived class is not called

Hello all,

i am stuck fixing why the base classes virtual method is not called.
The variable (arr) is initialated as the base class Entity. I now want to be able to assign any Entity derived class, what works fine. The problem is that I always get the overrides of the derived classes of my virtual method that it was initialized with.

Heres my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115



#include <iostream>
#include <string.h>
#include "../../mystring/mystr.hpp"

using namespace std;




class Entity {
public:
    char interpretation;
    bool walkable;
    bool hasdirection;

    virtual String erronwalk() const = 0;
    Entity(char interpret = 0, bool wkable = false, bool hasdir = false): interpretation{interpret}, walkable{wkable}, hasdirection{hasdir} {}
};




class Empty: public Entity {
public:
    Empty(): Entity('w', true) {}
    String erronwalk() const override{
        return "possible";
    }
};


class myKara: public Entity {
public:
    myKara(char dir = 'R'): Entity('K', false) {}

    String erronwalk() const override{
        return "cannot walk over myKara";
    }

};









class KaraWorld {

    void _preset(size_t, size_t);

    size_t _rows{};
    size_t _columns{};
    Entity **_map = (Entity **) new Entity*[_rows];


public:

    KaraWorld(): _map{(Entity **) new Entity*[10]}, _rows{10}, _columns{10} { _preset(10, 10); };
    KaraWorld(size_t row_s, size_t column_s): _map{(Entity **) new Entity*[row_s]}, _rows{row_s}, _columns{column_s}  { _preset(row_s, column_s); }
    KaraWorld(size_t row_s): _map{(Entity **) new Entity*[row_s]}, _rows{row_s}, _columns{row_s}  { _preset(row_s, row_s); }
    KaraWorld(const KaraWorld& cp): _map{cp._map}, _columns{cp._columns}, _rows{cp._rows} {}
    KaraWorld(KaraWorld&& other) {std::swap(_map, other._map); std::swap(_rows, other._rows); std::swap(_columns, other._columns);}
    ~KaraWorld() {for (size_t i = 0; i < _rows; i++) {delete[] _map[i];} delete[] _map;}


    const size_t rows() const {return _rows;}
    const size_t columns() const {return _columns;}

    Entity **map() {return _map; }

    KaraWorld& put(size_t x, size_t y, Entity& obj);


};

void KaraWorld::_preset(size_t r, size_t c) {
    for (size_t i = 0; i < r; i++) {
        _map[i] = new Empty[c];

    }

}

KaraWorld& KaraWorld::put(size_t x, size_t y, Entity& obj) {
    _map[x][y] = obj;
    return *this;
}

ostream& operator<<(ostream& os, KaraWorld& k) {
    for (size_t m = 0; m < k.columns() * 2 + 1; m++)
        os << "_";
    os << "\n";
    for (size_t i = 0; i < k.rows(); i++) {

        for (size_t j = 0; j < k.columns(); j++) {

            os << "|";
            os << k.map()[i][j].interpretation << ((j + 1) == k.columns() ? "|\n":"");

        }

    }
    for (size_t m = 0; m < k.columns() * 2 + 1; m++)
        os << "_";
    os << "\n";

    return os;

}


usage:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include "myKara.hpp"
using namespace std;

int main() {
    KaraWorld kara{10, 10};
    myKara kar{};
    kara.put(3, 3, kar);
    cout << kara.map()[3][3].erronwalk() << "\n";
    cout << kara;


}


output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
possible
_____________________
|w|w|w|w|w|w|w|w|w|w|
|w|w|w|w|w|w|w|w|w|w|
|w|w|w|w|w|w|w|w|w|w|
|w|w|w|K|w|w|w|w|w|w|
|w|w|w|w|w|w|w|w|w|w|
|w|w|w|w|w|w|w|w|w|w|
|w|w|w|w|w|w|w|w|w|w|
|w|w|w|w|w|w|w|w|w|w|
|w|w|w|w|w|w|w|w|w|w|
|w|w|w|w|w|w|w|w|w|w|
_____________________


but there shouldnt be "possible", but "cannot walk over myKara".



Thanks in advance,

Luke



Polymorphism - via virtual - only works when called via a ref or a pointer.
You've discovered https://en.wikipedia.org/wiki/Object_slicing

_map[3][3] is an Empty, the actual object, not a pointer to one.
_map[x][y] = obj; slices the Entity part off the top of your myKara and copies that part only into the Empty.


To prevent this common mistake, people often make base classes non-copyable/non-assignable (e.g. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-copy-virtual in the C++ Core Guidelines).

Adding Entity& operator=(const Entity&) = delete; inside class Entity would be sufficient to catch it
Last edited on
Ive yust done that, but i have no idea how to assign obj to my _map[x][y] pointers
An example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

class Base {
public:
	virtual void log() const = 0;
};


class Derv1 : public Base {
public:
	void log() const override { std::cout << "derv1\n"; }
};

class Derv2 : public Base {
public:
	void log() const override { std::cout << "derv2\n"; }
};

int main() {
	Base* classes[] { new Derv1, new Derv2 };

	for (const auto& c : classes)
		c->log();

	for (const auto& c : classes)
		delete c;
}



derv1
derv2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>
#include <string>
#include <vector>
#include <memory>

struct Entity {

    char interpretation ;
    bool walkable ;
    bool hasdirection ;

    explicit Entity(char interpret = ' ', bool wkable = false, bool hasdir = false)
        : interpretation{interpret}, walkable{wkable}, hasdirection{hasdir} {}

    virtual ~Entity() = default ; // we need a virtual destructor

    // If you declare any copy, move, or destructor function, declare them all
    // https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-five
    Entity( const Entity& ) noexcept = default ;
    Entity( Entity&& ) noexcept = default ;
    Entity& operator= ( const Entity& ) noexcept = default ;
    Entity& operator= ( Entity&& ) noexcept = default ;

    virtual std::string erronwalk() const = 0;
};

struct Empty: Entity {

    Empty(): Entity('w', true) {}
    std::string erronwalk() const override {

        return "possible";
    }
};

struct myKara: public Entity {

    explicit myKara(): Entity('K', false) {}

    std::string erronwalk() const override{

        return "cannot walk over myKara";
    }
};

struct KaraWorld {

    std::vector< std::vector< std::unique_ptr<Entity> > > _map ;

    KaraWorld( std::size_t x, std::size_t y ) : _map(x) {

             for( auto& row : _map ) row.resize(y) ;
    }

    KaraWorld& preset( std::size_t x, std::size_t y ) { *this = KaraWorld(x,y) ; return *this ; }

    std::size_t rows() const noexcept { return _map.size() ; }
    std::size_t columns() const noexcept { return rows() > 0 ? _map[0].size() : 0 ; }

    const auto& map() const noexcept { return _map; }

    KaraWorld& put( std::size_t x, std::size_t y, std::unique_ptr<Entity> pobj ) {

        if( x < rows() && y < columns() ) {

            _map[x][y] = std::move(pobj) ;
        }

        return *this ;
    }

    friend std::ostream& operator<< ( std::ostream& stm, const KaraWorld& kw ) {

        const std::string line( kw.columns()*2 + 1, '-' ) ;
        stm << line << '\n' ;

        for( const auto& row : kw.map() ) {

            stm << '|' ;
            for( const auto& ptr : row ) stm << ( ptr ? ptr->interpretation : '.' ) << ' ' ;
            stm << "|\n" ;
        }

        return stm << line << '\n' ;
     }
};

int main() {

    KaraWorld kara( 1, 1 ) ;
    kara.preset( 10, 10 ) ;

    for( std::size_t r = 0 ; r < kara.rows() ; ++r )
        for( std::size_t c = 0 ; c < kara.columns() ; ++c )
            kara.put( r, c, std::make_unique<Empty>() ) ;

    for( std::size_t i : { 1, 3, 5, 7, 9 } ) kara.put( i, i, std::make_unique<myKara>() ) ;

    std::cout << kara << '\n' ;

    for( std::size_t i = 0 ; i < 6 ; ++i )
        std::cout << '(' << i << ',' << i << ") " << kara.map()[i][i]->erronwalk() << '\n' ;
}

http://coliru.stacked-crooked.com/a/2231bf8fdad53eaa
Thank you so much!, thats great
Polymorphism - via virtual - only works when called via a ref or a pointer.

😕

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Base
{
public:
    void foo()
    {
        std::cout << "Base::foo()" << std::endl;
        bar();
    }
protected:
    virtual void bar()
    {
        std::cout << "Base::bar()" << std::endl;
    }
};

class Derived : public Base
{
protected:
    virtual void bar()
    {
        std::cout << "Derived::bar()" << std::endl;
    }
};

int main()
{
    Derived instance;
    instance.foo();
}

Base::foo()
Derived::bar()
Last edited on
@kigar64551
One could argue that bar() on line 7 is "called via pointer" because it's equivalent to this->bar().
Last edited on
Topic archived. No new replies allowed.