oo-problem

J

Jan Boehme

Hi!
Can anyone tell me why the output of this program is in both cases "from
A!" an not "from B!" or "from C!" ?

Thanks a lot for helping me,
Jan.

//CODE///////////////////////////////////////////////////////

#include <iostream>
using namespace std;

class AImpl;
class A
{
public:
A();
virtual ~A();
const char * GetAddress();
private:
AImpl *pImpl;
};

class BImpl;
class B : public A
{
public:
B();
virtual ~B();
private:
BImpl *pImpl;
};

class CImpl;
class C : public A
{
public:
C();
virtual ~C();
private:
CImpl *pImpl;
};

class AImpl
{
public:
char a[2000];
AImpl()
{
strcpy(a, "from A!");
};
~AImpl(){};
const char * GetAddress()
{
return a;
}
};

class BImpl : public AImpl
{
public:
BImpl()
{
strcpy(a, "from B!");
};
~BImpl(){};
};

class CImpl : public AImpl
{
public:
CImpl()
{
strcpy(a, "from C!");
};
~CImpl(){};
};

A::A()
{
pImpl = new AImpl();
}

const char * A::GetAddress()
{
return pImpl->GetAddress();
}

B::B()
{
pImpl = new BImpl();
}

C::C()
{
pImpl = new CImpl();
}

A::~A()
{
delete pImpl;
}

B::~B()
{
delete pImpl;
}

C::~C()
{
delete pImpl;
}

int main(int argc, char* argv[])
{
B m_xyz;
cout << m_xyz.GetAddress() << endl;
C m_uvw;
cout << m_xyz.GetAddress() << endl;
return 0;
}
 
H

Hendrik Belitz

Jan said:
Hi!
Can anyone tell me why the output of this program is in both cases "from
A!" an not "from B!" or "from C!" ?

Thanks a lot for helping me,
Jan.

//CODE///////////////////////////////////////////////////////

#include <iostream>
using namespace std;

class AImpl;
class A
{
public:
A();
virtual ~A();
const char * GetAddress();
private:
AImpl *pImpl;
};

class BImpl;
class B : public A
{
public:
B();
virtual ~B();
private:
BImpl *pImpl;
};

class CImpl;
class C : public A
{
public:
C();
virtual ~C();
private:
CImpl *pImpl;
};

class AImpl
{
public:
char a[2000];
AImpl()
{
strcpy(a, "from A!");
};
~AImpl(){};
const char * GetAddress()
{
return a;
}
};

class BImpl : public AImpl
{
public:
BImpl()
{
strcpy(a, "from B!");
};
~BImpl(){};
};

class CImpl : public AImpl
{
public:
CImpl()
{
strcpy(a, "from C!");
};
~CImpl(){};
};

A::A()
{
pImpl = new AImpl();
}

const char * A::GetAddress()
{
return pImpl->GetAddress();
}

B::B()
{
pImpl = new BImpl();
}

C::C()
{
pImpl = new CImpl();
}

A::~A()
{
delete pImpl;
}

B::~B()
{
delete pImpl;
}

C::~C()
{
delete pImpl;
}

int main(int argc, char* argv[])
{
B m_xyz;
cout << m_xyz.GetAddress() << endl;
C m_uvw;
cout << m_xyz.GetAddress() << endl;
return 0;
}

You redefined pImpl in B and C. Since you dont overwrite your virtual method
GetAddress() in B and C, only the implementation of A will be used. This
will result in
const char * A::GetAddress()
{
return A::pImpl->GetAddress();
}
^-- !!!

For the behavior you desire, you need to overwrite getAddress() in B and C.
BTW, it's quite bad to create data members of the same name in base and
derived classes. Avoiding this will also avoid your problem.
 
C

Chris Theis

Jan Boehme said:
Hi!
Can anyone tell me why the output of this program is in both cases "from
A!" an not "from B!" or "from C!" ?

Thanks a lot for helping me,
Jan.
[SNIP]

You have to keep in mind that inheriting class B from class A results in B
having a part of A incorporated, which is what you normally want. Now you
supply the GetAddress() function in class A only which operates on the
implementation pointee of A! Thus calling this function from the derived
class B (without overriding it!) it's perfectly okay that the operation is
performed on the implementation pointee of A as this part is included in
class B. What you have to do is to supply the same function also in class B
operating on the pImpl pointer of B to get the behavior you expect. BTW it's
not a very good practice to have variables of the same name in base &
derived classes. This normally leads to confusion which you have already
experienced. Some other remarks: the dtor of AImpl should be virtual as you
derive from this class and don't forget to handle copy ctor & assignment in
your A, B, C classes or the next problem will be waiting around the corner.

HTH
Chris
 
J

Jan Boehme

Hendrik said:
You redefined pImpl in B and C. Since you dont overwrite your virtual method
GetAddress() in B and C, only the implementation of A will be used. This
will result in
const char * A::GetAddress()
{
return A::pImpl->GetAddress();
}
^-- !!!

For the behavior you desire, you need to overwrite getAddress() in B and C.
BTW, it's quite bad to create data members of the same name in base and
derived classes. Avoiding this will also avoid your problem.

I partly understand your explanation und I know how to solve the problem.
Alternatively I added a function to set the address-value a in the base
class.
Why this don't work?

Thanks, Jan.

#include <iostream>
using namespace std;

class AImpl;
class A
{
public:
A();
virtual ~A();
const char * GetAddress();
void SetAddress(const char * par);
private:
AImpl *pImpl;
};

class BImpl;
class B : public A
{
public:
B();
virtual ~B();
private:
BImpl *pImpl;
};

class CImpl;
class C : public A
{
public:
C();
virtual ~C();
private:
CImpl *pImpl;
};

class AImpl
{
public:
char a[2000];
AImpl()
{
SetAddress("from A!");
};
~AImpl(){};
const char * GetAddress()
{
return a;
}
void SetAddress(const char * par)
{
strcpy(a, par);
}
};

class BImpl : public AImpl
{
public:
BImpl()
{
SetAddress("from B!");
};
~BImpl(){};
};

class CImpl : public AImpl
{
public:
CImpl()
{
SetAddress("from C!");
};
~CImpl(){};
};

A::A()
{
pImpl = new AImpl();
}

const char * A::GetAddress()
{
return pImpl->GetAddress();
}

void A::SetAddress(const char * par)
{
pImpl->SetAddress(par);
}

B::B()
{
pImpl = new BImpl();
}

C::C()
{
pImpl = new CImpl();
}

A::~A()
{
delete pImpl;
}

B::~B()
{
delete pImpl;
}

C::~C()
{
delete pImpl;
}

int main(int argc, char* argv[])
{
B m_xyz;
cout << m_xyz.GetAddress() << endl;
C m_uvw;
cout << m_xyz.GetAddress() << endl;
return 0;
}
 
J

Jan Boehme

Hi!

Chris said:
You have to keep in mind that inheriting class B from class A results in B
having a part of A incorporated, which is what you normally want. Now you
supply the GetAddress() function in class A only which operates on the
implementation pointee of A! Thus calling this function from the derived
class B (without overriding it!) it's perfectly okay that the operation is
performed on the implementation pointee of A as this part is included in
class B. What you have to do is to supply the same function also in class B
operating on the pImpl pointer of B to get the behavior you expect. BTW it's
not a very good practice to have variables of the same name in base &
derived classes. This normally leads to confusion which you have already
experienced. Some other remarks: the dtor of AImpl should be virtual as you
derive from this class and don't forget to handle copy ctor & assignment in
your A, B, C classes or the next problem will be waiting around the corner.

Thanks for your explanation. This is only a small snippet to focus to
the main problem. I already thought the copy ctors & assignments are the
problem and tried it out.
But why it don't work by adding also a set-method to the base class and
call in the c'tors of the derived classes. It should perform the
operation at the implementation pointee of A?
Thanks, Jan.
 
C

Chris Theis

Jan Boehme said:
Hi!
[SNIP]
Thanks for your explanation. This is only a small snippet to focus to
the main problem. I already thought the copy ctors & assignments are the
problem and tried it out.
But why it don't work by adding also a set-method to the base class and
call in the c'tors of the derived classes. It should perform the
operation at the implementation pointee of A?
Thanks, Jan.

Try the following in your code just for a test. Declare the pImpl member in
class B public and add the following line to main:

cout << m_xyz.pImpl->a << endl;
cout << m_xyz.GetAddress() << endl;

What you'll get is that the line will deliver "from B" whereas the second
statement will get you "from A". Thus you can see that the value for pImpl
inside B has been set correctly, but calling GetAddress() obviously operates
on the part of class A which is contained inside B, as already explained.

HTH
Chris
 
A

Andreas Mueller

Jan said:
Hendrik said:
You redefined pImpl in B and C. Since you dont overwrite your
virtual method GetAddress() in B and C, only the implementation of
A will be used. This will result in
const char * A::GetAddress()
{
return A::pImpl->GetAddress();
}
^-- !!!

For the behavior you desire, you need to overwrite getAddress() in
B and C. BTW, it's quite bad to create data members of the same
name in base and derived classes. Avoiding this will also avoid
your problem.

I partly understand your explanation und I know how to solve the
problem. Alternatively I added a function to set the address-value a
in the base class.
Why this don't work?
[snip]


int main(int argc, char* argv[])
{
B m_xyz;
cout << m_xyz.GetAddress() << endl;
C m_uvw;

Because you are printing m_xyz two times :)
cout << m_xyz.GetAddress() << endl; // <====

//you probably meant:
cout << m_uvw.GetAddress() << endl;
 
A

Andreas Mueller

Jan said:
Hi!
Can anyone tell me why the output of this program is in both cases
"from A!" an not "from B!" or "from C!" ?

Thanks a lot for helping me,
Jan.

[snip]

Hi,

besides the solved problems with your first implementation, why are you
redefining a pimpl pointer in each derived class? This means that A will
carry around a AImpl, B an AImpl from A and a BImpl, and so on.

As you derive your Implementation hiearchy paralell to the "interface"
hiearchy, you can do it this way:

#include <iostream>
class AImpl;
class A
{
public:
A();
virtual ~A();
const char * GetAddress();
protected://A does the imp handling for you
void SetImp(AImpl* imp);
private:
AImpl *pImpl;
};

class BImpl;
class B : public A
{
public:
B();
virtual ~B();
};

class CImpl;
class C : public A
{
public:
C();
virtual ~C();
};

class AImpl
{
public:
AImpl()
{
SetA("from A!");
};
virtual ~AImpl(){};//virtual dtor!!
const char * GetAddress()
{
return a;
}
void SetA(const char* aa)
{
a = aa;
}
private:
const char* a;
};

class BImpl : public AImpl
{
public:
BImpl()
{
SetA("from B!");
};
virtual ~BImpl(){};
};

class CImpl : public AImpl
{
public:
CImpl()
{
SetA("from C!");
};
virtual ~CImpl(){};
};

A::A()
{// A uses AImpl
SetImp(new AImpl());
}
void A::SetImp(AImpl* imp)
{
pImpl = imp;
}
const char * A::GetAddress()
{
return pImpl->GetAddress();
}

B::B()
{//B uses BImpl, which is derived from AImpl
SetImp(new BImpl());
}

C::C()
{//C uses CImpl, which is indirectly derived from AImpl
SetImp(new CImpl());
}

A::~A()
{
delete pImpl;
}

B::~B()
{
}

C::~C()
{
}

int main(int argc, char* argv[])
{
B m_xyz;
std::cout << m_xyz.GetAddress() << std::endl;
C m_uvw;
//You really meant this or is it a typo?
//cout << m_xyz.GetAddress() << endl;
std::cout << m_uvw.GetAddress() << std::endl;
return 0;
}

HTH,
Andy
 
J

Jan Boehme

Hi Andreas!

Andreas said:
As you derive your Implementation hiearchy paralell to the "interface"
hiearchy, you can do it this way:

A::A()
{// A uses AImpl
SetImp(new AImpl());
}
void A::SetImp(AImpl* imp)
{
pImpl = imp;
}
const char * A::GetAddress()
{
return pImpl->GetAddress();
}

B::B()
{//B uses BImpl, which is derived from AImpl
SetImp(new BImpl());
}

C::C()
{//C uses CImpl, which is indirectly derived from AImpl
SetImp(new CImpl());
}

Your solution looks just like the way I was looking for. I started to
break up this parallel inheritance and began to put all the stuff in one
Impl class and create only an outer class hierachie.
Thanks for helping me out here.
Jan.

PS: Yes, I meant m_xyz.GetAddress() AND m_uvw.GetAddress(). That was a typo.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,163
Latest member
Sasha15427
Top