|
-
August 10th, 2012, 05:32 AM
#31
Re: Interview Questions
 Originally Posted by ninja9578
@OReubens Wouldn't making all of your destructors virtual mean that all of your objects have vtables?
Yes...
So it adds a 4 byte (8 on 64bit) table. big deal
So it adds a few bytes of extra code to deal with this virtual destructor. big deal.
In the big light of things, and with a growing trend for VM's, and intermediate languages. The above will as I stated have little impact on the totality of your program. There are very few cases where that virtual will cause unwanted effects.
FORGETTING the virtual when it was needed otoh. can have significant effects on your program.
Is it ideal. no. but that can be said about a lot of programming habits you do "just in case".
it's also a good habit to initialize every single member variable in your class. Even if you never end up using them. and even when you know that when you will be needing it, you're setting it to a valid value anyway. That's also code wasted. but somewhere down the line maintaining your code, it's a good habit anyway. 
Yes, if your class is designed to be a polymorphic base class, then it should either have a virtual destructor that is public, or a non-virtual destructor that is protected. At the same time, if a class has virtual functions, it is likely to be designed to be a polymorphic base class. As such, I find the insistence that such a factor has no relevance at all to be an exaggeration.
Even it the class is NOT designed to be a polymorphic base class. If you are making class libraries that other will be using, you are only restricting their possibilities by not doing this.
For this very same reason, unless you have VERY VERY good reason to, it's probably a bad idea to mark your classes as sealed. You may not see a need to derive further from them, but someone using your classes may have different ideas/needs.
I find it strange you're calling it an exaggeration when it is obviously correct. There IS NO direct correlation between a class having (or not having) virtual functions and the need for a virtual destructor. It is perfectly possible for a class to have no virtual functions at all and yet needing a virtual destructor to function properly. (written and used plenty of them). It is equally possible to have a class with virtual members and NOT needing a virtual destructor because even though the class is used through a base pointer does not imply that it will be deleted through a base pointer.
I will sort of agree that a class with virtual members is a likely candidate for needing a virtual destructor as well (without conclusions about the opposite). But that relation is much much less of a formal rule than than say the relation between a class that has a copy constructor also needing an assign operator (or vice versa).
Maybe not but what happens to your dignity? Before you know it you will be programming in Java!
Heh. 
There are worse choices to loose your dignity in.
Either way, I'm happy i don't need to program in java, although I occasionally need to read/review it.
-
August 10th, 2012, 09:16 AM
#32
Re: Interview Questions
 Originally Posted by OReubens
Also, there is very little lost by getting into a habit of making ALL your destructors virtual. It can save a lot of headaches somewhere down the line.
Are you advocating to actually make virtual destructor for any class even if it does not have any other virtual methods?
-
August 10th, 2012, 10:32 PM
#33
Re: Interview Questions
 Originally Posted by D_Drmmr
Why would it do that? I don't want to get a warning message whenever I derive, say, a function object from std::unary_function.
Well, a good IDE would allow you to switch off such warning if you didn't like them. And even switch them off selectively for say structs and std classes.
For example the Eclipse Java IDE issues error messages when you break the rules of the language of course, but it also keeps track of best practices of the language and issues warnings if you don't follow them. There are hundreds of such "meta" warnings each of which can be enabled/disabled individually.
This ability to enforce C++ best practices of your choise is something I miss in VC++.
Last edited by nuzzle; August 10th, 2012 at 10:36 PM.
-
August 11th, 2012, 05:07 AM
#34
Re: Interview Questions
 Originally Posted by OReubens
Yes...
So it adds a 4 byte (8 on 64bit) table. big deal
So it adds a few bytes of extra code to deal with this virtual destructor. big deal.
Are you sure about this?
A vtable is an implementation detail and not something a C++ compiler is required to add by the standard. Couldn't it be possible during a whole program optimization to remove vtables (or parts thereof) that are never used?
Java compilers can do this. In Java methods are virtual by default but if they're not used virtually they're defacto treated as if they were non-virtual thus removing any overhead.
-
August 11th, 2012, 07:10 PM
#35
Re: Interview Questions
Last edited by TheGreatCthulhu; August 11th, 2012 at 07:12 PM.
-
August 12th, 2012, 01:30 AM
#36
Re: Interview Questions
 Originally Posted by TheGreatCthulhu
I've seen how much you hate Java back when you went to the Java forums and posted a rant about it
Sure I've been ranting but that's mostly just a way of learning. When you get flak from everywhere you're forced to sharpen your argumentation. And I've calmed down here at this forum. 
I'm actually very fond of Java. There have been issues over the years but they've been technology shortcomings mostly, nothing to do with the language per se. And Java has improved immensely over the years and probably continued to do so the last couple of years while I've been using C++ exclusively.
The cultural difference you mention between Java and C++ programmers is real in my experience. That's why I'm prepared to give OReubens right in principle. For certain types of applications the typical C++ programmer's tendency to spend lots of time trying to reach small performance gains is counter-productive. This effort will be drowning in the whole of the application anyway and the only noticeable result will be unsafe and brittle code.
My main objection to OReubens suggestions (virtual destructors as standard and general initializations of variables) is that it's too little. If one wants to use C++ in a more Java-like fashion one should and easily could do more. For example a general application-wide reference counting scheme would be a given thing.
Regarding the Java compiler, yes I meant JIT. But some information a Java compiler must wait for until runtime, a C++ compiler has available statically already. At least if a whole program compilation is made. So maybe a statical analysis could allow a compiler to treat virtual language constructs as non-virtual if they're used non-virtually throughout. It's not that far-fetched. Then OReubens suggestion would both increase safety and be for free. Maybe someone knows for sure.
Last edited by nuzzle; August 12th, 2012 at 02:28 AM.
-
August 12th, 2012, 05:26 AM
#37
Re: Interview Questions
 Originally Posted by OReubens
Yes...
So it adds a 4 byte (8 on 64bit) table. big deal
So it adds a few bytes of extra code to deal with this virtual destructor. big deal.
In the big light of things, and with a growing trend for VM's, and intermediate languages. The above will as I stated have little impact on the totality of your program. There are very few cases where that virtual will cause unwanted effects.
First of all, I don't understand what virtual machines or intermediate languages have to do with C++.
Second, it is a big deal when your class is, say, an iterator that only wraps a single pointer. If all of the classes in the STL would have virtual destructors, I would certainly not be happy. There is no reason to use these classes in a runtime polymorphic way, so they don't need a virtual destructor. Adding one can cause major performance penalties. Here's a little test I did with VS2005. The cost of this premature pessimization in this case is a factor 3.8 performance loss. That's a big deal!
Code:
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <vector>
#include <windows.h>
const int ArraySize = 1000000;
struct Array
{
std::vector<int> data;
struct iterator {
explicit iterator(int* p = NULL) : ptr(p) {}
virtual ~iterator() {}
int& operator *() const { return *ptr; }
int* operator ->() const { return ptr; }
iterator& operator ++() { ++ptr; return *this; }
iterator operator ++(int) { iterator copy(*this); ++(*this); return copy; }
bool operator ==(const iterator& that) const { return ptr != that.ptr; }
bool operator !=(const iterator& that) const { return !(*this == that); }
private:
int* ptr;
};
Array() : data(ArraySize) {}
iterator begin() { return iterator(&data[0]); }
iterator end() { return iterator(&data[0] + ArraySize); }
};
int main()
{
std::srand(std::time(0));
Array a[500];
LARGE_INTEGER start, stop, freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
for (int loop = 0; loop < 1000000; ++loop) {
for (int i = 0; i < 500; ++i) {
for (Array::iterator it = a[i].begin(), end = a[i].end(); it != end; ++it) {
*it = std::rand();
}
}
for (int i = 0; i < 500; ++i) {
double sum = 0.;
for (Array::iterator it = a[i].begin(), end = a[i].end(); it != end; ++it) {
sum += *it;
}
}
}
QueryPerformanceCounter(&stop);
std::cout << double(stop.QuadPart - start.QuadPart) / freq.QuadPart << std::endl;
}
// results of 5 runs with the virtual destructor in iterator
6.91767
6.90869
6.93206
6.91705
6.91032
// results of 5 runs when the virtual destructor in iterator is removed
1.962
1.97473
1.97738
1.96257
1.97505
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
August 12th, 2012, 05:30 AM
#38
Re: Interview Questions
 Originally Posted by nuzzle
Well, a good IDE would allow you to switch off such warning if you didn't like them. And even switch them off selectively for say structs and std classes.
My point is that such a warning would hardly ever be useful. There would be way too many cases for which it would produce nothing but an annoying bogus message. A more useful warning IMO is something like this: http://msdn.microsoft.com/en-us/libr...(v=vs.80).aspx.
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
August 12th, 2012, 09:05 AM
#39
Re: Interview Questions
 Originally Posted by D_Drmmr
The cost of this premature pessimization in this case is a factor 3.8 performance loss. That's a big deal!
... moreover, with SCL iterators turned off, it's a 2x size penalty ( 3x if D_Drmmr composed his iterator with an std::vector::iterator with its own virtual dtor, (1+N)x with iterators made of N iterators compositions ( unless inheritance is (awkwardly) used ), etc ... ).
Hence, I agree with D_Drmmr, it can be a big deal, especially with generic code where you have to maximize optimal scalability both in space and time, as you don't know how your type will be instanced, you just know its expected semantics.
For example, if you give me a vector<T> I expect it to be used muldimensionally in an optimal way: so I can write, say, vector<vector<... vector<T>>>: in this case, a (useless) pointer per instance would mean a sizeof(void*)*( N^(D-1) + N^(D-2) + ... + 1 ) overhead, with dimension D and mean size per dimension N ( and note that a vector has a size of the same order of magnitude of a pointer; therefore, if sizeof(T)~sizeof(void*) the resulting overhead would not be negligible with respect to its "optimized" size ).
just to show that these things matter in the real world too, here is a quote from the vc2012 development team:
Finally, in addition to numerous bugfixes, we've performed a major optimization! All of our containers (loosely speaking) are now optimally small given their current representations. This is referring to the container objects themselves, not their pointed-to guts. For example, vector contains three raw pointers. In VC10, x86 release mode, vector was 16 bytes. In VC11, it's 12 bytes, which is optimally small. This is a big deal if you have 100,000 vectors in your program - VC11 will save you 400,000 bytes. Decreased memory usage saves both space and time.
This was achieved by avoiding the storage of empty allocators and comparators, as std::allocator and std::less are stateless. (We'll activate these optimizations for custom allocators/comparators too, as long as they're stateless. Obviously, we can't avoid storing stateful allocators/comparators, but those are quite rare.)
now, maybe there's some marketing at play here, but at least it shows that there's a strong request from the community for these kind of optimizations ...
 Originally Posted by nuzzle
Couldn't it be possible during a whole program optimization to remove vtables (or parts thereof) that are never used?
even ignoring binary compatibility issues, you would need an unbelievably smart compiler to achieve that: just consider that the size of a type ( however compiler dependent ) must be known and consistent for complete types in each translation unit, independently on how that type is "used" in them. BTW, what do you mean by "parts of the vtable" ? note that the actual vtable is stored just once in any case ...
Last edited by superbonzo; August 12th, 2012 at 09:09 AM.
-
August 13th, 2012, 01:52 AM
#40
Re: Interview Questions
 Originally Posted by D_Drmmr
First of all, I don't understand what virtual machines or intermediate languages have to do with C++.
They're examples of how Java and C# trades efficiency for other gains such as a higher degree of programmer safety than you find in C++.
If I get OReubens correctly he's suggesting to "borrow" some safety features of those languages and apply them to the context of OO programming in C++. This would include the liberal use of virtual destructors as well as initialization of variables throughout. My only objection to this approach is that it's not far-reaching enougth. In my view as a minimum every application object also should be reference-counted.
So it's not about spoiling C++ by introducing mandatory virtual destructors in the language. It's a design question. You want to write an OO application (or support libraries for OO applications) in C++ and you want to make it as safe as possible. What measures do you take?
Regarding your example. If you add ~iterator() {} it degrades performance as much as virtual ~iterator() {} does (at least with my VS2010 compiler and the settings I'm using). So this example doesn't show especially a virtual destructor will degrade performance. It shows a user added default destructor, virtual or non-virtual alike, may degrade performance more than an automatically generated one depending on the compiler implementation.
Last edited by nuzzle; August 13th, 2012 at 05:24 AM.
-
August 13th, 2012, 04:57 AM
#41
Re: Interview Questions
 Originally Posted by superbonzo
now, maybe there's some marketing at play here, but at least it shows that there's a strong request from the community for these kind of optimizations ...
I don't see how OReubens' suggestion goes against "these kinds of optimizations". On the contrary, his suggestion would be even better if everything declared virtual would be optimized into non-virtual when used non-virtually. This would be fully in the spirit of C++. You pay for what you use.
When MS manages to shave a few extra bytes out of its vector implementation it's good news of course. More of that in all of STL. Lean and mean standard libraries is what everyone expects and demands.
But I doubt this reduction of the overhead of vector will have much impact on the typical C++ application. And if you use a lot of vectors it's probably more important to make them shrink-to-fit. Having 100.000 vectors each with just 100 bytes of excess capacity on average would total 10.000.000 bytes. That's a lot of wasted memory and dwarfs any gain from the leaner vector implementation.
Last edited by nuzzle; August 13th, 2012 at 05:29 AM.
-
August 13th, 2012, 12:15 PM
#42
Re: Interview Questions
As nuzzle said, this is a question of software design. Design principles and practices, as well as best practices in general, are not set in stone, and are not to be followed with religious zeal: they all have some reasoning behind them, their pros and cons, and thus a defined domain of applicability. If the requirements for your software put it outside of this domain (or not entirely within it), you make some effort to gain a deeper understanding of the system you're building, and then either modify the design principle/practice, or throw it away entirely as inapplicable in favor of something else.
Are many of you going to develop alternatives to the STL? The concerns you're expressing about the virtual destructor appear to me to be related to rather specific cases - cases where the practice shouldn't, but more importantly, where it doesn't have to be followed. No one's forcing you to do anything. A principle/practice should be considered on the basis of its applicability, and it should be estimated how relevant and beneficial it is in general (what nuzzle called "far-reaching", if I understood correctly). Once you've made that estimation, you can decide how often and in what cases you are going to follow this principle/practice (if at all).
Whether the compiler should issue a warning or not is a different matter.
OReubens' argument was that it "can save a lot of headaches somewhere down the line", and that the cases "where a virtual destructor would actually cause problems and where it needs to be non-virtual are very rare". All you have to do now is decide if you agree with this observation or not.
But that's from your personal experience and most likely from the perspective of a field you're working in. Language (and compiler) designers, on the other hand, have to analyze how the language is (or is going to be) truly used in the majority of cases (possibly giving some important cases more weight than others), and make language design (or compiler design) decisions based on that, including whether to support a principle or a best practice through the language, through compiler warnings or otherwise, doing their best not to jeopardize how people use the language for other things. Tricky business...
Last edited by TheGreatCthulhu; August 13th, 2012 at 12:22 PM.
-
August 14th, 2012, 02:40 AM
#43
Re: Interview Questions
 Originally Posted by TheGreatCthulhu
No one's forcing you to do anything.
I disagree. Correct me if I'm wrong, but the whole discussion was about the possibility of enforcing ( at the language level or as a self-imposed rule ) virtual destructors on all types ( being designed for inheritance or not ) as a precautionary measure against unwanted UB.
If OReubens' view was just "for types whose foreseeable usage would imply public inheritance, make the destructor virtual unless there's a very good reason to not do so" then I think everybody would agree with him. But this is not the case ( unless I misinterpreted his words, of course ).
 Originally Posted by nuzzle
I don't see how OReubens' suggestion goes against "these kinds of optimizations". On the contrary, his suggestion would be even better if everything declared virtual would be optimized into non-virtual when used non-virtually. This would be fully in the spirit of C++. You pay for what you use.
first of all, as I alluded in my previous post, such global optimization is just nearly-impossible with the c++ compilation model and a dynamic type implementation that can influence the size of types. So, in essence, OReubens' suggestion does imply a size overhead on polymorphic types and hence does go against such micro-size optimizations.
Moreover, I'd say that the spirit of C++ is not just "you pay for what you use", it's more something like "you pay for what you want". That is, I think most C++ programmers would be nervous at managing types whose size, binary content and behavior could change in response to a code change in a different traslation unit ...
 Originally Posted by nuzzle
But I doubt this reduction of the overhead of vector will have much impact on the typical C++ application. And if you use a lot of vectors it's probably more important to make them shrink-to-fit. Having 100.000 vectors each with just 100 bytes of excess capacity on average would total 10.000.000 bytes. That's a lot of wasted memory and dwarfs any gain from the leaner vector implementation.
yes, but if design decisions were just a matter of plain asymptotics, then essentially all languages would be the same. Sometimes, small effects do add up to produce noticable effects, especially with bounded resources or when there is a non trivial relation between resouces (time,space,...) and the perceived "value" of the final product; in such a case, being able of fine tuning code translates in greater value.
For example, consider GUI responsivity: the curve that relates, say, the time needed to visually feedback a user action and the user perception of its delay is non linear, like a steep ramp. Now, what if your 1.0 release stays near the edge of that ramp ? a C++ programmer would have the highest chance ( with respect to other programmers ) of moving from that edge both by fine tuning code but also proactively, by choosing designs that combine good abstractctions <and> optimal behavior. BTW, is this why managed apps are universally perceived as "slower" ?
-
August 14th, 2012, 08:11 AM
#44
Re: Interview Questions
 Originally Posted by nuzzle
So it's not about spoiling C++ by introducing mandatory virtual destructors in the language. It's a design question. You want to write an OO application (or support libraries for OO applications) in C++ and you want to make it as safe as possible. What measures do you take?
As superbonzo, I also read OReubens' suggestion as using virtual destructors for every class (or struct), even those that have no virtual functions and, therefore, are not designed to be used in an OO way. That just doesn't make sense to me. IMO, you gain nothing and potentially loose a lot.
I have never found it a problem to decide which classes need a virtual destructor (after I learned about them, that is). Every base class that has virtual functions gets a virtual destructor - no problem there. Similarly, I often find it relatively easy to figure out which class or object is responsible for the memory management of a certain object. Only in those cases where shared ownership makes sense, have I used shared_ptr (or intrusive_ptr with an internal reference count where performance really mattered). When there is a single owner, I'd say the use of reference counting is premature pessimization. You don't need it, and it can hurt you, not just in terms of performance, but it also opens the possibility to circular dependencies. There are plenty of other smart pointers (unique_ptr if you can use it, I'm stuck with boost::scoped_ptr) that you can use to prevent memory leaks or dead pointers. However, the most important guideline to prevent all of these problems is not to use OO for everything in C++.
 Originally Posted by nuzzle
Regarding your example. If you add ~iterator() {} it degrades performance as much as virtual ~iterator() {} does (at least with my VS2010 compiler and the settings I'm using). So this example doesn't show especially a virtual destructor will degrade performance. It shows a user added default destructor, virtual or non-virtual alike, may degrade performance more than an automatically generated one depending on the compiler implementation.
If every class should/must have a virtual destructor, you don't have the option of using the compiler implementation.
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
August 17th, 2012, 05:11 PM
#45
Re: Interview Questions
 Originally Posted by superbonzo
I disagree. Correct me if I'm wrong, but the whole discussion was about the possibility of enforcing ( at the language level or as a self-imposed rule ) virtual destructors on all types ( being designed for inheritance or not ) as a precautionary measure against unwanted UB.
It's definately not about enforcing anything at the language level. C++ is C++ and stays C++.
It's about using virtual destructors as a self-imposed standard in the OO part of an OO dominated application. As a precautionary safety measure they may as well be introduced throughout even for classes at the perimeter of OO.
But I suggest more. I suggest all OO objects also should be reference counted by default in an application-wide memory management scheme. This approach is quite common today even in C++ libraries. The C++ Direct3D library has it and the C++ Metro library will too.
It's important to realize that C++ is a very versatile multi-paradigmic language that can be used very differently. Some usages will want to favour general safety over efficiency in every detail. It's a treadoff that may not be as expensive as many would think. This idea has even spawned new languages such as Java and subsequently C#.
It's almost become a general wisdom that you cannot or shouldn't program in C++ like you do in Java. I don't share this view. One of the major design goals of Java was to be a safer C++ and I believe the findings can be successfully adopted back to C++. I'm doing that, I'm using the Java programming style in C++. It works fine and I'm still reaping the full power of C++. Perfect.
Last edited by nuzzle; August 18th, 2012 at 05:39 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|