问题
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