class call {
public:
long GetCallID();
void SetCallID(long callid);
private:
long m_CallID;
};
class tapicall : public call
{
public:
long GetTapiID();
void SetTapiID(long tapiid);
private:
long m_TapiID;
};
class iface
{
public:
call* FindCallByID(long callid);
void CreateNewCall(long callid);
void DeleteCall(long callid);
private:
std::list<call*> m_calllist;
};
Implementation here:
#include "call.h"
long call::GetCallID(){
return m_CallID;
}
void call::SetCallID(long callid){
m_CallID = callid;
}
long tapicall::GetTapiID(){
return m_TapiID;
}
void tapicall::SetTapiID(long tapiid){
m_TapiID = tapiid;
}
call* iface::FindCallByID(long callid){
std::list<call*>::const_iterator it;
for(it = m_calllist.begin(); it != m_calllist.end(); ++it){
if((*it)->GetCallID() == callid)
return *it;
}
return 0;
}
void iface::CreateNewCall(long callid){
call* pCall = new call;
pCall->SetCallID(callid);
m_calllist.push_back(pCall);
}
void iface::DeleteCall(long callid){
//do later
}
Then if I do this (in order as below):
void CInherittestDlg::OnOK()
{
m_iface.CreateNewCall(1);
}
void CInherittestDlg::OnBtnSet()
{
tapicall* ptcall = reinterpret_cast<tapicall*>(m_iface.FindCallByID
(1));
if(ptcall) {
ptcall->SetTapiID(3);
}
}
void CInherittestDlg::OnBtnGet()
{
long tapiid;
tapicall* ptcall = reinterpret_cast<tapicall*>(m_iface.FindCallByID
(1));
if(ptcall) {
tapiid=ptcall->GetTapiID();
}
}
Then I get the correct value back.
My understanding problem is that m_iface.FindCallByID returns a
call*. A call* stored in the std::list has only enough memory
reserved to be a call*
And we simply cast to a tapicall* and get the 'extra' bit which is the
TapiID. How does this work at a low level? Surely what is stored in
the iface list is only a call*?
Puzzled as to how it works.
+ code mostly equivalent to
tapicall* p = reinterpret_cast<tapicall*>( new call );
// Doing tapicall things here.
> Then I get the correct value back.
You have undefined behavior.
>
> My understanding problem is that m_iface.FindCallByID returns a
> call*. A call* stored in the std::list has only enough memory
> reserved to be a call*
The pointed to object may or may not be large enough to accomodate
more data than a 'call'. But regardless you have undefined behavior,
accessing memory that's not guaranteed to belong to the pointed to
object.
Cheers & hth.,
- Alf
The place where I work uses this sort of technique a lot. Are you
saying this is not recommended?
Undefined Behavior is never recommended. Even when it apparently
"works" the slightest change, in source code or tool usage, can
produce a different effect, so it's a ticking bomb. However, a
sufficiently cynical, incompetent and dishonest manager or project
lead may hold the view that meeting a deadline is much more important
than delivering robust code, and that the buried problems may be
desirable because they'll likely yield at least one "fix it" contract.
If this is the case (weak management) then it's likely that your
workplace's code has at least a few functions of many hundred lines.
And if so then there's no hope of getting the technical right, and it
may then be dangerous to one's career to even mention it.
Cheers & hth. (even if slightly off-topic),
- Alf
[Concerning accessing unallocated memory...]
> The place where I work uses this sort of technique a lot. Are
> you saying this is not recommended?
He said it was undefined behavior. It's not a question of
"recommended"; undefined behavior means that it doesn't work, or
that it may not work all of the time. Frankly, if you're doing
this a lot, I'm surprised you haven't had core dumps.
I'd strongly recommend testing your code with Purify or
valgrind.
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Here, only call objects are created and pushed to the list. No tapicall
object is created anywhere.
> }
>
> void iface::DeleteCall(long callid){
> //do later
> }
>
>
> Then if I do this (in order as below):
>
> void CInherittestDlg::OnOK()
> {
> m_iface.CreateNewCall(1);
> }
>
> void CInherittestDlg::OnBtnSet()
> {
> tapicall* ptcall = reinterpret_cast<tapicall*>(m_iface.FindCallByID
> (1));
As no tapicall objects have been ever created, this reinterpret_cast
produces undefined behavior (unless the pointer is NULL).
> if(ptcall) {
> ptcall->SetTapiID(3);
> }
> }
>
> void CInherittestDlg::OnBtnGet()
> {
> long tapiid;
> tapicall* ptcall = reinterpret_cast<tapicall*>
(m_iface.FindCallByID
> (1));
> if(ptcall) {
> tapiid=ptcall->GetTapiID();
> }
> }
>
> Then I get the correct value back.
>
> My understanding problem is that m_iface.FindCallByID returns a
> call*. A call* stored in the std::list has only enough memory
> reserved to be a call*
True, but irrelevant. What counts it the size of the object the call*
pointer points to. A valid call* pointer can point either to a call
object or a tapicall object (or be NULL). If there were some entries
created as 'new tapicall' and reinterpret_cast were used only on those
entries, then everything would be fine.
>
> And we simply cast to a tapicall* and get the 'extra' bit which is the
> TapiID. How does this work at a low level? Surely what is stored in
> the iface list is only a call*?
True, but irrelevant. The objects themselves are not part of the list.
>
> Puzzled as to how it works.
As it stands in your example, it doesn't.
Regards
Paavo