* Re: vtable on stack
@ 2010-02-02 13:27 Frank Winter
2010-02-02 15:04 ` Axel Freyn
0 siblings, 1 reply; 13+ messages in thread
From: Frank Winter @ 2010-02-02 13:27 UTC (permalink / raw)
To: axel-freyn; +Cc: gcc-help
Hi Axel!
Thank you!
Using references is a good idea. Indeed, B::f() gets called.
I agree that making the object const, is quite a drawback and having a
temporary object living parallel is that good either.
--
Frank
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: vtable on stack
2010-02-02 13:27 vtable on stack Frank Winter
@ 2010-02-02 15:04 ` Axel Freyn
2010-02-02 18:13 ` Patrick Horgan
0 siblings, 1 reply; 13+ messages in thread
From: Axel Freyn @ 2010-02-02 15:04 UTC (permalink / raw)
To: gcc-help
Hi Frank,
> Using references is a good idea. Indeed, B::f() gets called.
>
> I agree that making the object const, is quite a drawback and having a
> temporary object living parallel is that good either.
There is also another possibilty you could maybe think about: If you
create an additional structure, which "caches" the pointer to A or B and
returns references to it, you can have this new structure living on the
stack - and do with it whatever you want (e.g. you can insert it into a
std::vector without problems):
struct Cache{
Cache( A* _a):a(_a){}
A *a;
A& operator()(){ return *a;}
};
int main() {
A* x = new B();
x->f(); // Class B
Cache y = new B();
y().f(); // Class A
}
this prints also "Class B".
However, as always there are some drawbacks...
- my example will introduce memory-leaks, as the pointers are never
deleted -> add a destructor to "Cache"
- if you want to copy "y", you have to be careful as to what you are
doing (shall the object pointed to be "a" also be copied - or shall
both "Cache"'s use the same A? If you copy it: Which
copy-constructor - the one of A or the one of B? If not: then each
Cache has to count, how often the object A is used - in order to be
able to write a working destructor...
Maybe the most powerful way would be:
- add a virtual function "A* make_copy()" to struct A, which returns a
copy of A
- overload it in struct B
- overload the Copy-Constructor and the assignement-operator for struct
Cache, such that they use "make_copy" (this guarantees that
"Cache y = new B(); Cache y1 = y;" and "Cache y = new A(); Cache y1
= y" are treated differently.
- Add a destructor to Cache, which deletes the pointer
- If - in addition - you also add "const A& operator()()const", you can
also use "Cache" when it is a constant.
With that, you would simulate quite well a "virtual class on the stack"
- you can do almost everything with it. The only drawback I see is the
necessity to explicitely write "y()" instead of "y" (or some arbitrary
other memberfunction like "*y" or "y.get()"), which returns the
reference to A*.
Axel
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: vtable on stack
2010-02-02 15:04 ` Axel Freyn
@ 2010-02-02 18:13 ` Patrick Horgan
2010-02-02 20:39 ` Axel Freyn
0 siblings, 1 reply; 13+ messages in thread
From: Patrick Horgan @ 2010-02-02 18:13 UTC (permalink / raw)
To: gcc-help
Axel Freyn wrote:
> There is also another possibilty you could maybe think about: If you
> create an additional structure, which "caches" the pointer to A or B and
> returns references to it, you can have this new structure living on the
> stack - and do with it whatever you want (e.g. you can insert it into a
> std::vector without problems):
>
> struct Cache{
> Cache( A* _a):a(_a){}
> A *a;
> A& operator()(){ return *a;}
> };
>
I like this whole thing, but wouldn't operator* be more natural to
return the reference?
Patrick
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: vtable on stack
2010-02-02 18:13 ` Patrick Horgan
@ 2010-02-02 20:39 ` Axel Freyn
2010-02-02 21:35 ` Patrick Horgan
0 siblings, 1 reply; 13+ messages in thread
From: Axel Freyn @ 2010-02-02 20:39 UTC (permalink / raw)
To: gcc-help
Hi Patrick,
On Tue, Feb 02, 2010 at 09:56:19AM -0800, Patrick Horgan wrote:
>> There is also another possibilty you could maybe think about: If you
>> create an additional structure, which "caches" the pointer to A or B and
>> returns references to it, you can have this new structure living on the
>> stack - and do with it whatever you want (e.g. you can insert it into a
>> std::vector without problems):
>> struct Cache{
>> Cache( A* _a):a(_a){}
>> A *a;
>> A& operator()(){ return *a;}
>> };
>>
> I like this whole thing, but wouldn't operator* be more natural to
> return the reference?
Even if we are probably quite off-topic now: Yes, you are absolutely
right. I only used () as, when I first (already a few years ago) wrote a
similar class (a bit more complex) which I wanted to use for fast
matrix-vector calculations, I realized a problem with "*": it can lead
to unresolved overloads when you write something like
class GeneralCache { ... };
class Cache : public GeneralCache { ... };
... operator * (const GeneralCache &, const GeneralCache & );
Cache a, b, c;
c = a * b;
Now is "*" dereferencing b, or is it multiplying a and b?
I don't remember the precise definitions I used - but at least g++
claimed it would have been an unresolvable overloading (And my reading
and partial understanding of the C++-standard did confirm this). Of
course, by adding parenthesis or temporaries it would be possible to
solve the problem, but I switched to operator() ...
Axel
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: vtable on stack
2010-02-02 20:39 ` Axel Freyn
@ 2010-02-02 21:35 ` Patrick Horgan
0 siblings, 0 replies; 13+ messages in thread
From: Patrick Horgan @ 2010-02-02 21:35 UTC (permalink / raw)
To: gcc-help
Axel Freyn wrote:
> Hi Patrick,
> On Tue, Feb 02, 2010 at 09:56:19AM -0800, Patrick Horgan wrote:
>
>>> There is also another possibilty you could maybe think about: If you
>>> create an additional structure, which "caches" the pointer to A or B and
>>> returns references to it, you can have this new structure living on the
>>> stack - and do with it whatever you want (e.g. you can insert it into a
>>> std::vector without problems):
>>> struct Cache{
>>> Cache( A* _a):a(_a){}
>>> A *a;
>>> A& operator()(){ return *a;}
>>> };
>>>
>>>
>> I like this whole thing, but wouldn't operator* be more natural to
>> return the reference?
>>
> Even if we are probably quite off-topic now: Yes, you are absolutely
> right. I only used () as, when I first (already a few years ago) wrote a
> similar class (a bit more complex) which I wanted to use for fast
> matrix-vector calculations, I realized a problem with "*": it can lead
> to unresolved overloads when you write something like
> class GeneralCache { ... };
> class Cache : public GeneralCache { ... };
> ... operator * (const GeneralCache &, const GeneralCache & );
>
Yes, I see this, but this is the nature of the beast. Even without user
defined types you can get exactly this same problem and sometimes need
parenthesis to make clear what you mean. When you override operator()
people expect the meaning to be function or constructor.
> Cache a, b, c;
> c = a * b;
> Now is "*" dereferencing b, or is it multiplying a and b?
>
Frustrating when you try to come up with a good example off the top of
your head and you can't think of one, huh? It always happens to me
too. Of course this would only make sense for the binary operator*, the
unary would one just be a syntax error, and current gcc would use the
binary one without warnings.
Patrick
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: vtable on stack
2010-02-02 13:47 ` Frank Winter
@ 2010-02-02 13:53 ` John (Eljay) Love-Jensen
0 siblings, 0 replies; 13+ messages in thread
From: John (Eljay) Love-Jensen @ 2010-02-02 13:53 UTC (permalink / raw)
To: Frank Winter, Kevin P. Fleming; +Cc: gcc-help
Hi Frank,
> You mentioned a vector of references. As far as I [know] this is not possible.
You are correct, a std::vector of C++ references is not possible.
You would have to use a std::vector of pointers, or a std::vector of smart pointers. (Sometimes smart pointers are called references, just to muddy the water.)
For smart pointers, I recommend Boost's smart pointers:
http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/smart_ptr.htm
Sincerely,
--Eljay
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: vtable on stack
2010-02-02 13:40 ` Kevin P. Fleming
@ 2010-02-02 13:47 ` Frank Winter
2010-02-02 13:53 ` John (Eljay) Love-Jensen
0 siblings, 1 reply; 13+ messages in thread
From: Frank Winter @ 2010-02-02 13:47 UTC (permalink / raw)
To: Kevin P. Fleming; +Cc: John (Eljay) Love-Jensen, gcc-help
Hi Kevin!
>
> That's not quite correct here; you are not inserting a B into the vector
> at all. In the second call to push_back(), a temporary B object is
Thank you for pointing that out!
You mentioned a vector of references. As far as I now this is not
possible.
vector<A&> vec;
or
A& vec[N];
Correct me if I am wrong. But I believe this is illegal C++ code. The
compiler would have to transform a reference into a pointer.
--
Frank
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: vtable on stack
2010-02-02 12:45 ` Frank Winter
2010-02-02 13:11 ` Florian Weimer
@ 2010-02-02 13:40 ` Kevin P. Fleming
2010-02-02 13:47 ` Frank Winter
1 sibling, 1 reply; 13+ messages in thread
From: Kevin P. Fleming @ 2010-02-02 13:40 UTC (permalink / raw)
To: Frank Winter; +Cc: John (Eljay) Love-Jensen, gcc-help
Frank Winter wrote:
> Nobody would want to write this. But somebody might want to use objects
> with virtual functions and define hers object on the stack, like:
>
> vector<A> ve;
> ve.push_back(A());
> ve.push_back(B());
> ve.push_back(A());
>
> iterate over it and call ::f(). Then always A::f() gets called, because
> its a vector of A. But a B is also an A. Thatfor I can insert it into
> the vector.
That's not quite correct here; you are not inserting a B into the vector
at all. In the second call to push_back(), a temporary B object is
instantiated on the stack, and then A's copy constructor is called to
create an A from it in the space allocated inside the vector (then the
temporary B object is destroyed). The vector does *not* contain a B
object at any time. As has previously been mentioned, the only way to
use polymorphism with objects in containers is to use pointer-to-object
or reference-to-object semantics; if you use value semantics, then the
objects will be sliced to the type that the container is defined to hold.
--
Kevin P. Fleming
Digium, Inc. | Director of Software Technologies
445 Jan Davis Drive NW - Huntsville, AL 35806 - USA
skype: kpfleming | jabber: kpfleming@digium.com
Check us out at www.digium.com & www.asterisk.org
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: vtable on stack
2010-02-02 12:45 ` Frank Winter
@ 2010-02-02 13:11 ` Florian Weimer
2010-02-02 13:40 ` Kevin P. Fleming
1 sibling, 0 replies; 13+ messages in thread
From: Florian Weimer @ 2010-02-02 13:11 UTC (permalink / raw)
To: Frank Winter; +Cc: John (Eljay) Love-Jensen, gcc-help
* Frank Winter:
> I think, its not possible to make use of the vtable with objects
> living on the stack.
This is not true. C++'s brand of polymorphism does not interact well
with copying (due to slicing). Your examples stress this point.
Put differently, you want to create an object of a type which is not
known at compile-time. This is not directly possible in C++.
Workarounds generally use the heap because unknown types have unknown
sizes, and objects of unknown size don't mix well with stack
allocation.
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: vtable on stack
2010-02-02 12:41 ` John (Eljay) Love-Jensen
2010-02-02 12:41 ` Axel Freyn
@ 2010-02-02 12:45 ` Frank Winter
2010-02-02 13:11 ` Florian Weimer
2010-02-02 13:40 ` Kevin P. Fleming
1 sibling, 2 replies; 13+ messages in thread
From: Frank Winter @ 2010-02-02 12:45 UTC (permalink / raw)
To: John (Eljay) Love-Jensen; +Cc: gcc-help
Dear Eljay,
thank you for your reply!
>
> To fix your example, do this:
> B y = B();
You are right. This creates a B on the stack and B::f() gets called.
Nevertheless, here virtual functions are not involved. The vtable is not
used when calling y.f()
My example created an A on the stack, and assigned a B to it, without
turning the A into a B. Right!
But, I wanted to use virtual functions with class objects purley living
on the stack _without_ doing some sort of casting:
B y = B();
dynamic_cast<A*>(&y)->f(); // B::f() called
(*(A*)(&y)).f(); // B::f() called
Nobody would want to write this. But somebody might want to use
objects with virtual functions and define hers object on the stack, like:
vector<A> ve;
ve.push_back(A());
ve.push_back(B());
ve.push_back(A());
iterate over it and call ::f(). Then always A::f() gets called, because
its a vector of A. But a B is also an A. Thatfor I can insert it into the
vector.
This example can only be realized with
vector<A*>.
I think, its not possible to make use of the vtable with objects
living on the stack.
--
Frank
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: vtable on stack
2010-02-02 12:11 Frank Winter
@ 2010-02-02 12:41 ` John (Eljay) Love-Jensen
2010-02-02 12:41 ` Axel Freyn
2010-02-02 12:45 ` Frank Winter
0 siblings, 2 replies; 13+ messages in thread
From: John (Eljay) Love-Jensen @ 2010-02-02 12:41 UTC (permalink / raw)
To: Frank Winter, gcc-help
Hi Frank,
> Do virtual functions work with class objects created on the stack?
Yes.
> Consider the example below.
I think you may be misunderstanding this line:
A y = B();
That makes an A on the stack, since y is an A. y does not become a B from the assignment. Rather B is sliced to an A.
So that A y = B(); is not analogous to:
A* x = new B();
To fix your example, do this:
B y = B();
Sincerely,
--Eljay
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: vtable on stack
2010-02-02 12:41 ` John (Eljay) Love-Jensen
@ 2010-02-02 12:41 ` Axel Freyn
2010-02-02 12:45 ` Frank Winter
1 sibling, 0 replies; 13+ messages in thread
From: Axel Freyn @ 2010-02-02 12:41 UTC (permalink / raw)
To: gcc-help
Hi Frank,
On Tue, Feb 02, 2010 at 04:06:56AM -0800, John (Eljay) Love-Jensen wrote:
> Hi Frank,
>
> > Do virtual functions work with class objects created on the stack?
>
> Yes.
>
> > Consider the example below.
>
> I think you may be misunderstanding this line:
> A y = B();
>
> That makes an A on the stack, since y is an A. y does not become a B
> from the assignment. Rather B is sliced to an A.
>
> So that A y = B(); is not analogous to:
> A* x = new B();
>
> To fix your example, do this:
> B y = B();
Another possibility would be to use references and write:
const A &y = B();
(the "const" is necessary, as you can't create a non-const reference to
a temporary object (that's a limitation I really don't like - sometimes
it would be nice to have:-)). Of course, the drawback is that y is
constant, so you have to define the member functions as:
virtual void f() const { cout << "Class A" << endl; }
and
void f() const { cout << "Class B" << endl; }
Then, y.f() outputs "Class B".
A third way is to use an additional variable, where you store the
B-object (so it's no temporary, and you can create a non-constant
reference to it), and then create a reference of type A to it:
B y1 = B();
A & y = y1;
Then, y refers to an object of type B and "y.f()" outputs "Class B".
I think, it depends on your situation what is the "best" solution.
Axel
^ permalink raw reply [flat|nested] 13+ messages in thread
* vtable on stack
@ 2010-02-02 12:11 Frank Winter
2010-02-02 12:41 ` John (Eljay) Love-Jensen
0 siblings, 1 reply; 13+ messages in thread
From: Frank Winter @ 2010-02-02 12:11 UTC (permalink / raw)
To: gcc-help
Do virtual functions work with class objects created on the stack?
Consider the example below.
First the class object is created on the heap and the f() call binding
is done via vtable as supposed.
Second, the class lives on the stack. B is created, assignment operator
to A called. But the f() call prints "class A". Maybe here the vtable is
not copied..?
I used structs here to avoid con/destructor calls.
How would one use virtual functions with objects living on the stack. Is
it possible at all?
--
Frank
#include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "Class A" << endl; }
};
struct B: A {
void f() { cout << "Class B" << endl; }
};
int main() {
A* x = new B();
x->f(); // Class B
A y = B();
y.f(); // Class A
}
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2010-02-02 20:41 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-02-02 13:27 vtable on stack Frank Winter
2010-02-02 15:04 ` Axel Freyn
2010-02-02 18:13 ` Patrick Horgan
2010-02-02 20:39 ` Axel Freyn
2010-02-02 21:35 ` Patrick Horgan
-- strict thread matches above, loose matches on Subject: below --
2010-02-02 12:11 Frank Winter
2010-02-02 12:41 ` John (Eljay) Love-Jensen
2010-02-02 12:41 ` Axel Freyn
2010-02-02 12:45 ` Frank Winter
2010-02-02 13:11 ` Florian Weimer
2010-02-02 13:40 ` Kevin P. Fleming
2010-02-02 13:47 ` Frank Winter
2010-02-02 13:53 ` John (Eljay) Love-Jensen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).