C++ Confused by this code with polymorphism, pointers and object slicing

假如想象 提交于 2019-12-25 12:41:45

问题


I'm trying to understand how polymorphism, object slicing and pointers work in this block of code. I'm working in Visual Studio.

#include <iostream>

class Man {
public:

    virtual void speak() { std::cout << "I'm a man." << "\n"; }

private:

};

class Soldier : public Man {
public:

    virtual void speak() { std::cout << "I'm a soldier." << "\n"; }

private:

};

int main() {

    Man man1;
    Soldier soldier1;

    man1 = soldier1;
    std::cout << "Man1: "; man1.speak();

    Man *man2 = new Man;
    Man *soldier2 = new Soldier;

    man2 = soldier2;
    std::cout << "Man2: "; (*man2).speak();

    Man *man3 = new Man;
    Soldier *soldier3 = new Soldier; // "Man *soldier3 = new Soldier;" will give the same output.

    *man3 = *soldier3;
    std::cout << "Man3: "; man3->speak();

    return 0;

}

The output is:

Man1: I'm a man.
Man2: I'm a soldier.
Man3: I'm a man.

I did some searching and learned about the concept "object slicing". I guess that is what has happened to man1 and man3. But wait, didn't we use the keyword "virtual" in all of those classes? Shouldn't man1 and *man3 first find out what class of object they each are, and call the specific overriding speak()?

Or is it because slicing has already happened at the = operator, at the line:

man1 = soldier1;

And the line:

*man3 = *soldier3;

And man1 and *man3 are really just Man objects now?

A fellow coder guessed it is because the = operator is only assigning the right-hand-side value to a copy of the left-hand-side variable. It needs to be confirmed still.

Aside from those, the goal I want to achieve is to copy that Soldier object to a different memory address, unlike how I point two pointers at one same memory address, in the case of man2 and soldier2.

Finally, I wonder why slicing doesn't happen in part2, and what really happens at syntax like this:

Man *soldier2 = new Soldier;

Seriously what does it do..?

I appreciate any insight on this. I'm a basic C++ programmer :) <


回答1:


didn't we use the keyword "virtual" in all of those classes

The virtual keyword is unrelated to object slicing.

Shouldn't man1 and *man3 first find out what class of object they each are, and call the specific overriding speak()?

No, they cannot and they should not. C++ has a certain object model. It includes objects occupying a certain region of memory. There's no place in this model for object assignment working like you expected it to work.

Consider this modification to a soldier class:

class Soldier : public Man {
public:

    virtual void speak() { std::cout << "I'm a soldier with a" << weapon << "\n"; }

    Soldier (const std::string& w) : weapon(w) {}

private:

    std::string weapon;

};

Now you have Soldier objects occupying more space than Man objects. When you assign

man1 = soldier1

there's simply no place inside man1 to fit the weapon string, so it gets cut off. Now soldier's speak cannot possibly work because it would not be able to find the weapon, so man's speak is used.

When you assign pointers, there's no cut off because a Man pointer is perfectly capable of pointing to a Soldier. At the raw memory level all pointers are essentially the same and they don't care what to point at. This is why polymorphism can work.

Some (including myself) would argue that since object slicing is almost always an error, an attempt to invoke it should cause a compile-time error rather than confusing silent breakage. But the language isn't currently defined this way, so you have to watch out.




回答2:


Or is it because slicing has already happened at the = operator, at the line: ...

There is indeed slicing at man1 = soldier1, but this does not affect the man3/soldier3 case. Slicing in this case is exactly the same as when you dereference the pointers with man3 / soldier3. See below.

When you say man2 = soldier2 you are saying "set the address stored in the pointer man1 to the address stored in soldier1". This does NOT cause address slicing, the memory location pointed to by man1 is now a soldier.

HOWEVER, when you say *man3 = *soldier3 you are telling the computer to assign the value of man3 in memory to the value stored in soldier3. In other words, you are saying "Take the value stored in the memory location pointed to by soldier3, and store it in the memory location pointed to by man3." (By the way, this is called "dereferencing" the pointer). The problem is the memory needed to store a man is too small to hold a soldier, because a soldier is a man plus some other data. Therefore the compiler slices the new soldier data off before storing it in the man memory location.

Finally, I wonder why slicing doesn't happen in part2, and what really happens at syntax like this:

The reason this doesn't cause slicing basically is because C++ is designed to work this way with pointers. The actual size of all pointers is the same on a given architecture, and the compiler knows that a soldier "is a" man. Because you declared your function virtual, the compiler knows to use the correct override for that function.




回答3:


In two words: Soldier virtual member-functions are called only when you have an instance of Soldier class. You may access it via Man class pointer or reference pointing to Soldier object. When you assign or copy Soldier variable to Man variable, only base class Man is copied, because the target variable cannot accommodate Soldier class.



来源:https://stackoverflow.com/questions/45773839/c-confused-by-this-code-with-polymorphism-pointers-and-object-slicing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!