473,395 Members | 1,457 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

Unexpected behaviour with virtual base classes

Hi,

I am observing unexpected behaviour, in the form of a corrupted class
member access, from a simple C++ program that accesses an attribute
declared in a virtual base class via a chain of virtual method calls.
To further complicate (or perhaps simplify) matters, some compilers
(GCC and MingW) produce the expected behaviour, while others (MSVS 7.1)
do not. I can only offer two explanations for my observations:

1. The Microsoft compiler (Microsoft Visual Studio 7.1) is producing
incorrect code (unlikely).

2. My code is non-compliant (or subject to some "grey" area not covered
by the standard) causing the compilers to exhibit undefined behaviour
(more likely).

I managed to narrow the problem down to a test involving five classes
and two methods. Although my example might seem pathological, it is
based on a larger piece of code, that uses an external toolkit, which
exhibited the exact same (unexpected) behaviour. I managed to divorce
the code from the external toolkit and trimmed it down dramatically
whilst retaining the unexpected behaviour. I am unable to reduce it any
further without removing the unexpected behaviour.

The code, consisting of a single C++ source file, as well as batch
files to build it using MSVS and GCC compilers are provided below. I
built it using Microsoft Visual Studio 7.1 (C++ compiler version
13.10.3077) and GCC 3.2.3 (from MinGW).

The outputs of the programs, from the Microsoft and GCC compilers, are
shown below. It shows the values of an object's "this" pointer, as well
as the address and value of an integer attribute referenced by the
"this" pointer, within two contexts (i.e. C's and B1's), the one
context being a base class context with respect to the other one. In
the case of the code produced by the MinGW compiler, the values and
addresses are identical in both contexts (as I had hoped). However, in
the case of the code produced by the Microsoft compiler, the values of
the "this" pointers differ, leading to an incorrect attribute value
being returned. Experimentation has shown the value by which the "this"
pointer deviates from the "correct" value to be related to the size of
the most derived class (i.e. D).

Regards,

Gerhard Esterhuizen
Herewith the outputs and source and build files:
Output from MSVS compiled executable :
======================================

----------------------------------------
C::dump_addrs():
this = 0012FEC0

this_B1 = 0012FED4 // B1's "this" in C's context
&this_B1->i = 0012FED8
this_B1->i = 1234

B1::f():
this = 0012FED0 // differs from B1's "this" in C's context
&i = 0012FED4
i = 4399316

Output from MinGW compiled executable:
======================================

----------------------------------------
C::dump_addrs():
this = 0x22ff68

this_B1 = 0x22ff70 // B1's "this" in C's context
&this_B1->i = 0x22ff74
this_B1->i = 1234

B1::f():
this = 0x22ff70 // identical to B1's "this" in C's context
&i = 0x22ff74
i = 1234
Source code:
============

#include <iostream>

struct A
{
int dummy;

virtual void f() = 0;
};

struct B1 : virtual public A
{
int i;

B1() : i(1234)
{
}

virtual void f()
{
std::cerr << "B1::f():" << std::endl
<< " this = " << this << std::endl
<< " &i = " << &i << std::endl
<< " i = " << i << std::endl;
}
};

struct B2 : virtual public A
{
B2()
{
}

void g()
{
f();
}
};

struct C : virtual public B1,
public B2
{
C()
{
std::cerr << std::endl
<< "----------------------------------------" <<
std::endl;

dump_addrs();
g();
}

void dump_addrs()
{
B1* this_B1 = static_cast<B1*>(this);

std::cerr << "C::dump_addrs():" << std::endl
<< " this = " << this << std::endl
<< std::endl
<< " this_B1 = " << this_B1 << std::endl
<< " &this_B1->i = " << &this_B1->i << std::endl
<< " this_B1->i = " << this_B1->i << std::endl
<< std::endl;
}
};

struct D : public C
{
float val;
};

int main()
{
D d;
}

Oct 12 '05 #1
2 2147
Gerhard Esterhuizen wrote:
Hi,

I am observing unexpected behaviour, in the form of a corrupted class
member access, from a simple C++ program that accesses an attribute
declared in a virtual base class via a chain of virtual method calls.
To further complicate (or perhaps simplify) matters, some compilers
(GCC and MingW) produce the expected behaviour, while others (MSVS 7.1)
do not. I can only offer two explanations for my observations:

1. The Microsoft compiler (Microsoft Visual Studio 7.1) is producing
incorrect code (unlikely).
It certainly cannot be ruled out. The older versions of the Microsoft
compilers had a surprising number of problems. The more recent VS C++
compilers seem to have fixed many of at least the most serious ones.
2. My code is non-compliant (or subject to some "grey" area not covered
by the standard) causing the compilers to exhibit undefined behaviour
(more likely).

I managed to narrow the problem down to a test involving five classes
and two methods. Although my example might seem pathological, it is
based on a larger piece of code, that uses an external toolkit, which
exhibited the exact same (unexpected) behaviour. I managed to divorce
the code from the external toolkit and trimmed it down dramatically
whilst retaining the unexpected behaviour. I am unable to reduce it any
further without removing the unexpected behaviour.

The code, consisting of a single C++ source file, as well as batch
files to build it using MSVS and GCC compilers are provided below. I
built it using Microsoft Visual Studio 7.1 (C++ compiler version
13.10.3077) and GCC 3.2.3 (from MinGW).

The outputs of the programs, from the Microsoft and GCC compilers, are
shown below. It shows the values of an object's "this" pointer, as well
as the address and value of an integer attribute referenced by the
"this" pointer, within two contexts (i.e. C's and B1's), the one
context being a base class context with respect to the other one. In
the case of the code produced by the MinGW compiler, the values and
addresses are identical in both contexts (as I had hoped). However, in
the case of the code produced by the Microsoft compiler, the values of
the "this" pointers differ, leading to an incorrect attribute value
being returned. Experimentation has shown the value by which the "this"
pointer deviates from the "correct" value to be related to the size of
the most derived class (i.e. D).

Regards,

Gerhard Esterhuizen .... Source code:
============

#include <iostream>

struct A
{
int dummy;

virtual void f() = 0;
};

struct B1 : virtual public A
{
int i;

B1() : i(1234)
{
}

virtual void f()
{
std::cerr << "B1::f():" << std::endl
<< " this = " << this << std::endl
<< " &i = " << &i << std::endl
<< " i = " << i << std::endl;
}
};

struct B2 : virtual public A
{
B2()
{
}

void g()
{
f();
}
};
The implementation of B2::g() certainly looks like it has a problem.
Where is the implementation of f() that will be called from g()? The
only apparent possibility is A::f(), but A::f() has not been defined. I
would test whether defining A::f() resolves the problem.

Admittedly, it's odd that the compiler would not complain, but perhaps
virtual inheritance somehow prevents an error from being reported.
struct C : virtual public B1,
public B2
{
C()
{
std::cerr << std::endl
<< "----------------------------------------" <<
std::endl;

dump_addrs();
g();
}

void dump_addrs()
{
B1* this_B1 = static_cast<B1*>(this);

std::cerr << "C::dump_addrs():" << std::endl
<< " this = " << this << std::endl
<< std::endl
<< " this_B1 = " << this_B1 << std::endl
<< " &this_B1->i = " << &this_B1->i << std::endl
<< " this_B1->i = " << this_B1->i << std::endl
<< std::endl;
}
};


I don't really see what useful conclusion can be drawn from outputing
these pointer values. A pointer to an instance of a polymorphic type,
especially one with virtual inheritance, can and will vary depending on
the class scope in which it appears. Therefore two pointers that do not
reference the same address, may still compare as equal. The only way to
know whether the pointers are equal is to have the program test them
for equality.

Otherwise, the interpretation of these pointer values is
implementation-dependent and as such few meaningful conclusions can be
drawn from the values alone. In other words, since we have no way of
knowing what the pointer values should be, we have no way of telling
that the pointer values that we do observe, are anything other than
what they should be.

Greg

Oct 12 '05 #2
Hi Greg,

Thanks for the reply.

At this point any further debate seems fairly academic, as Albert
Strasheim pointed out to me that this is a known bug. Details at
http://lab.msdn.microsoft.com/produc...1-403ebf5085c8

Nontheless, I would still like to comment on your comments:

I don't see anything wrong with B2::g() calling f(): as f() is declared
virtual in a A, which is a base of B2, the implementation can reside in
any class derived from A (and does reside in B1, which is derived from
A). That's polymorhism at work.

As for your concerns about the usefulness of the pointer dumps: I agree
that you won't find any use comparing GCC's pointer values with MSVS's
(for the reasons you specified). However, for the output of each
compiler, I quote *two* sets of pointers and *two* accesses to the same
member (B1::i). The only difference is the contexts within which these
values are obtained (i.e. in the one case I am in a method of C, which
is derived from B1, and in the other case I am in a method of B1). In
the above example, the MSVS compiler shows B1::i's value to be either
1234 or 4399316, depending on the context, while GCC shows it as 1234
in both contexts (the expected behaviour). The different values shown
by the MS compiler is indicative of the problem (if I were to write to
B1::i from B1's context, I would surely corrupt some memory). I
apologise for not being clear enough about this in the original post.

Regards,

Gerhard

Oct 12 '05 #3

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

62
by: christopher diggins | last post by:
Since nobody responded to my earlier post , I thought I would try to explain what I am doing a bit differently. When multiply inheriting pure virtual (abstract) base classes, a class obviously...
8
by: LAvoisieR | last post by:
Following test code behaviour suprised me and I dont know what's wrong with this. I have two overloaded constructors within base class and virtual destructor implemented. Derived class uses...
14
by: JPRoot | last post by:
Hi I use the following syntax to have events inherited from base to child classes which works nicely (virtual and override keyword on events). But I am wondering if it is a "supported" way of using...
2
by: Heinz Ketchup | last post by:
Hello, I'm looking to bounce ideas off of anyone, since mainly the idea of using Multiple Virtual Inheritance seems rather nutty. I chalk it up to my lack of C++ Experience. Here is my...
11
by: Nindi73 | last post by:
A few days a ago I posted my code for a deep copy pointer which doesn't require the pointee object to have a virtual copy constructor. I need help with checking that it was exception safe and...
7
by: eric | last post by:
hello i'm confused by an example in the book "Effective C++ Third Edition" and would be grateful for some help. here's the code: class Person { public: Person(); virtual ~Person(); // see...
7
by: dragoncoder | last post by:
Hello experts, I was just playing around wrote this code. sundev1:/home/ptiwary/rnd $ cat a1.cpp #include <iostream> using namespace std; class Base
14
by: Hunk | last post by:
Hi I ws wondering if there is a way to implement operator+ in case of virtual classes. Here's the problem. I have to have a base string class from which two classes (normal char string and a...
3
by: Daniel Kraft | last post by:
Hi, I usually program in C++, but for a special project I do have to code in C (because it may be ported to embedded-like platforms where no C++ compiler exists). I do want to realize some...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.