#include <iostream>
usingnamespace std;
class Matris {
private:
int* m_vec;
public:
Matris() {
m_vec = newint[10];
for(int i=0; i < 10; i++) {
m_vec[i] = 0;
}
}
};
class ChessPiece {
public:
int x,y;
bool isWhite;
virtualchar utf8mb4Representation() = 0;
};
class Rook : public ChessPiece {
public:
Rook(int x, int y, bool isWhite) {
x = x;
y = y;
isWhite = isWhite;
}
virtualchar utf8mb4Representation() {
if(isWhite) {
return'R';
}
return'r';
}
};
int main(void) {
Matris s;
ChessPiece* piece = new Rook(0, 0, false);
cout<<piece->isWhite; //Prints 1 (wrong)
Rook piece2(0, 0, false);
cout<<piece2.isWhite; //Prints 0 (correct)
return 0;
}
Dynamically allocating a ChessPiece object (subclass Rook) and printing out the isWhite variable wrongly prints 1 (true) while allocating such an object on the stack instead and printing its isWhite variable out correctly prints 0 (false). Both objects are allocated with the same constructor arguments. I have found four ways to solve the problem: modifying the Rook constructor's parameter names to be different from the ChessPiece member variables, removing the dynamic allocation in the Matris constructor, and removing the virtual utf8mb4Representation() function.
I've read that compilers are smart enough to realize that when you write such a statement inside a constructor, it should assign the class member's value since it makes no sense to update the original variable to have the same value as it had before the update. I could be wrong, though. The troubling factor here may be that the member variables aren't declared explicitly inside Rook but are rather inherited from ChessPiece. The problem even goes away when I remove the x and y ChessPiece members.
You definitely have masking in effect. Perfectly logical.
1 2 3 4 5 6 7
Rook::Rook(int x, int y, bool isWhite)
{
// within this scope (of function body) the argument names mask members
x = x;
y = y;
isWhite = isWhite;
}
The reason that you get different (undefined) values is due to your platform's stack and free store having whatever they have.
One could use this:
1 2 3 4 5 6
Rook::Rook(int x, int y, bool isWhite)
{
this->x = x;
this->y = y;
this->isWhite = isWhite;
}
One could use explicit scope:
1 2 3 4 5
Rook(int x, int y, bool isWhite) {
ChessPiece::x = x;
ChessPiece::y = y;
ChessPiece::isWhite = isWhite;
}
However, the most logical solution is to delegate the construction of a ChessPiece to the constructor of ChessPiece:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class ChessPiece {
public:
ChessPiece( int x, int y, bool isWhite )
: x( x ), y( y ), isWhite( isWhite ) // unambiguous
{
}
};
class Rook : public ChessPiece {
public:
Rook( int x, int y, bool isWhite )
: ChessPiece( x, y, isWhite )
{
}
};
Unambiguous?
1 2 3 4 5 6
class Foo {
public:
Foo( /*args*/ )
: bar( gaz )
{ }
};
Q: Why could gaz have same identifier as bar in this?
A: The member initializer list is not the same scope as the the body of the function. The "bar" that is initialized in it has to resolve to data member of the class. However, the initializer "gaz" is first looked from the arguments. Hence no masking.
For human reader it is better to keep the argument name and the member name slightly different.
I've read that compilers are smart enough to realize that when you write such a statement inside a constructor, it should assign the class member's value since it makes no sense to update the original variable to have the same value as it had before the update. I could be wrong, though.
You are wrong. What you probably read - and misunderstood - is that in an initialisation list, you can use the same name for the data member and the constructor argument. This is because, in the initialisation list, the syntax makes it unambiguous when the data member is being referenced, and when the argument is being referenced.