473,851 Members | 2,167 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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 2173
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
3403
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 bloats quickly for each new vtable needed. Execution slows down considerably as well. You can work around this by using interfaces referemnces which have a pointer to the object and a pointer to an external function lookup table. This technique...
8
1771
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 protected non-parametrized constructor of base class which DOES NOT initialize private member 'value'. But by calling both destructors virtually, binary does not complaint about deleting uninitialized pointer. WHY ?? C++ compiler used: G++ 3.4.3 ...
14
12168
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 events since I never saw it used anywhere in MSDN documentation/samples?! Or it will just break when I upgrade to .NET Framework 2.x in the coming years namespace MyNamespac public delegate void MyDel() public class MyBase public virtual...
2
1872
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 scenario... I have 5 Derived Classes I have 3 Base Classes
11
3443
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 exception neutral/ I got a reply from Bux with his code for a smart pointer with far fewer lines of code and more cleaner design, not over engineered like mine. ...
7
3099
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 item 7 for why this is virtual ...
7
2087
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
2355
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 hash string class ) are derived. The two derived classes are template classes specifying the sizes. The base class is a non-template class so that it can be used generically in the interface classes. the design would look like
3
1636
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 kind of polymorphic behaviour, like this: I have some abstract base "class" Base, that is, a struct containing a vtable of function-pointers and possibly some common fields (but at the
0
9898
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9748
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
1
10735
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 Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9506
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7907
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5934
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4549
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
4143
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
3180
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

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.