Jump to content

The less asked questions

- - - - -

This topic has been archived. This means that you cannot reply to this topic.
26 replies to this topic

#1
ZekeDragon

ZekeDragon

    Writes binary right handed and hex left handed

  • Moderators
  • 2,103 posts
I've been having a problem recently.

I figured myself not to be a good C++ coder, in fact not to be even an okay one, but a passable one. One that could read and understand what was going on in most situations. I'm not intimately familiar with every niche, but I'm working on it. However, I've had a couple of experiences on here that have shaken my confidence even in that, and make me feel like a complete idiot. Situations I should have known and recognized simply by looking at them, because I've faced them before and utilized their abilities, and yet here I am again, either not paying enough attention or (worse) simply not knowing exactly what's going on.

So I start this thread, because I don't want to be in the dark on these issues. I don't want to not understand what's going on. I want to know EVERYTHING about C++. Every. Single. Thing. Nothing missing. I don't want to give other people wrong advice, I don't want to create improper algorithms, and I DON'T EVER want to say something is possible/impossible when it's the other way around.

I don't know what I can give, but I'll +rep as much as I can for people helping me out here. I really appreciate it. I have two questions for now, but I'll certainly have more later.

Question 1:
Why is checked_delete necessary, and what does it accomplish?


According to the Boost site, it explains that "The C++ Standard allows, in 5.3.5/5, pointers to incomplete class types to be deleted with a delete-expression." However, I was under the impression that an 'incomplete class type' would lead to a compile-time error, which wouldn't get out of the gate let alone have unusual run-time conditions. Anyway, it goes on to say "When the class has a non-trivial destructor, or a class-specific operator delete, the behavior is undefined." I understand how this supposedly causes a problem, but I'm still tripping up on the last part, when are you going to be in a run-time condition that necessitates deleting an incomplete class type with a non-trivial destructor?


Question 2:
What is with const throw() in some areas of the C++ STL?


I've had the opportunity to read quite a bit of the C++ STL and other standard libraries (Boost), but there have been situations where I am confused. I know what const means in every context I've used it, and I know what throw() means at the end of a function, but I don't see how they can work together. Throw() informs the compiler that the function will NOT throw an exception, and as such can optimize accordingly, but I don't see how in this context that const makes any sense. What does the const modifier do in this situation?


That's my questions for now. I hope you guys will be okay with my ignorance and my incessant questions. :)
Wow I changed my sig!

#2
ZekeDragon

ZekeDragon

    Writes binary right handed and hex left handed

  • Moderators
  • 2,103 posts
FOLLOW UP: I already answered my own question two. I simply realized that the "const" had nothing to do with the "throw()" part. The const is referring to the fact that the function does not change any properties in the class, similarly to if throw() were left off. Again, I just didn't read it right.

Anyway, to not make this boring..

Question 3:
How does the Colvin-Gibbons Trick work, and what does it do to resolve the problem?


(This is more commonly known as the "Move Constructor" idiom) Everyone knows that using RAII to manage resources with objects is crucial in sufficiently large projects. This makes code more robust and severely reduces memory leaks, but in many situations, such as factory objects, you'll end up passing dynamically created objects that may outlast the object that generated them, or simply needs to be destroyed elsewhere. In order for memory management to work intuitively from there, you usually use something like std::auto_ptr, but there are conditions when you need to use the factory/other objects returned auto_ptr object immediately as an argument for a constructor. In this case, an error occurs, because the compiler will generate a temporary object from that returned argument, and by default that temporary object is const, and when the constructor doesn't take a const argument, you have a problem. What does this "trick" do to rectify the situation, because as far as I can see, it doesn't seem to assist in any way (though I may simply be reading it wrong again. O_o)?
Wow I changed my sig!

#3
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,715 posts
First question: Deleting an incomplete class type is allowed but causes undefined behavior. The people who wrote checked_delete() came up with a trick using sizeof() that forces a compile-time error if you try to delete an instance of an incomplete class. As to why you would need that...this is the only thing I can think of. I don't know if it's right, but I think this is kind of what they're getting at.

class Base {
protected:
    void *ptr;
public:
    Base(void* init) : ptr(init) {
        //do some init work here
    }
    virtual ~Base(void){
        cout << "Base destructor." << endl;
    }
};

//delete function here is a nop
class PointerToStackVar : public Base {
    PointerToStackVar(void *init) : Base(init) {
        //other stuff here
    }
    ~PointerToStackVar(void) {
        //nothing to do - can't delete a stack var
    }
};

class PointerToHeapVar : public Base {
public:
    PointerToHeapVar(void *init) : Base(void) {
        //other stuff here
    }
    ~PointerToHeapVar(void){
        //only works on primitives, not classes
        delete init;
    }
};

...
void cleanup(Base& ptr) {
    delete ptr;
}

Third question: Found a link here that explains the Colvin-Gibbons trick rather well. (Scroll down to Role of auto_ptr_ref.)

Edited by dargueta, 04 September 2009 - 04:17 AM.

sudo rm -rf /

#4
ZekeDragon

ZekeDragon

    Writes binary right handed and hex left handed

  • Moderators
  • 2,103 posts
Thank you dargueta, I imagine you'll be one of the people who would know a lot of these answers, along with (maybe) WingedPanther. From what responses you give to others that I read, you two are some of the best programmers I've met. But let me respond, I guess I need to rephrase my first question; how would you ever end up with an incomplete class type with a complex destructor? What conditions, in the code, would cause this, maybe a simple code example would help? I think that's what you were trying to provide in that code example, but to me it just seems to be a couple of very simple smart pointer classes that are derived from Base and I'm not sure how that applies to the situation. I'm sorry I'm being so demanding...

As far as question three goes, that helped a lot, thank you.

Question 4:
Why does a statically declared array not exhibit polymorphism?


It's a well known fact that when you have an array of a base type's pointers, you can have those pointers point to derived objects of that base object and use them as if they were that base object, and so long as the base class' function is declared virtual, any overridden function from the derived class will be used instead when the function is called. Essentially, it's polymorphism, and a simple example is below:
#include <iostream>

#include <string>


class HelloWorldClass

{

public:

    virtual void sayHello ()

    {

        std::cout << "Hello World!" << std::endl;

    }

};


class HelloPerson : public HelloWorldClass

{

    std::string myName;

public:

    HelloPerson(const char* n) : myName(n)

    {

    }


    void sayHello ()

    {

        std::cout << "Hello " << myName << ", how are you?" << std::endl;

    }

};


int main ()

{

    HelloWorldClass* helloArray[] = { new HelloPerson("Joe"),

                                      new HelloPerson("Dan"),

                                      new HelloWorldClass() };

    for (int iii = 0; iii < 3; ++iii)

        helloArray[iii]->sayHello();

    return 0;

}
This will cause three different outputs per call, depending on which object was called in the array. However, let's just change a small part of this, and instead of that array being an array of pointers to new objects, make it simply an array of HelloWorldClass objects:
#include <iostream>

#include <string>


class HelloWorldClass

{

public:

    virtual void sayHello ()

    {

        std::cout << "Hello World!" << std::endl;

    }

};


class HelloPerson : public HelloWorldClass

{

    std::string myName;

public:

    HelloPerson(const char* n) : myName(n)

    {

    }


    void sayHello ()

    {

        std::cout << "Hello " << myName << ", how are you?" << std::endl;

    }

};


int main ()

{

    HelloWorldClass helloArray[] = { HelloPerson("Joe"),

                                     HelloPerson("Dan"),

                                     HelloWorldClass() };

    for (int iii = 0; iii < 3; ++iii)

        helloArray[iii].sayHello();

    return 0;

}
This code compiles and runs perfectly well, however unexpected behavior is the result, you will notice that the program no longer exhibits any sort of polymorphism. I don't really understand why this doesn't work similarly to the first one (albeit without a memory leak), and I was hoping one of you did. Also, how should I go about doing something similar to this, or should I simply avoid doing anything similar to this altogether?
Wow I changed my sig!

#5
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,715 posts
I don't have access to a compiler at the moment, but try changing this

class HelloPerson : public HelloWorldClass

to this:

class HelloPerson : virtual public HelloWorldClass

It's a shot in the dark (i.e. I have no idea and I'm just guessing randomly), but it's worth a try.
sudo rm -rf /

#6
ZekeDragon

ZekeDragon

    Writes binary right handed and hex left handed

  • Moderators
  • 2,103 posts
Seems to have worked perfectly, nice random guess. I'll try and remember that extra virtual for when I need this next, my Question 4 is answered. :)

I'm gonna get some sleep. I'll have another question up in a few hours. I really appreciate all the help, darguetta!
Wow I changed my sig!

#7
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,715 posts
It worked?! :w00t: (And my name has one t in it...common mistake. The worst misspelling I've seen is Arquettq.)
sudo rm -rf /

#8
ZekeDragon

ZekeDragon

    Writes binary right handed and hex left handed

  • Moderators
  • 2,103 posts
Haha, that was a typo, not a spelling error (same thing?). :P Always spelled your name right before, like I said, I was tired. I'll leave it unfixed so people don't look at you funny.
Wow I changed my sig!

#9
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,715 posts
Thanks for taking my personal dignity into consideration. :)
sudo rm -rf /

#10
ZekeDragon

ZekeDragon

    Writes binary right handed and hex left handed

  • Moderators
  • 2,103 posts
Question 5:
Is it possible to send a type name or object name as an argument to a function, and if so, how?


When you're programming with C++, you're bound to find times when the standard library uses nothing more than types to identify what to do with a particular declared object or what will result. A commonly seen one is the new operator, which is nothing more than a standard library overloaded operator to malloc(), when it comes right down to it...
#include <iostream>
#include <typeinfo>
int main() {
    int* anum = new int;
    // int* anum = (*int) operator new (sizeof(int));
    std::cout << typeid(*anum).name() << std::endl << typeid(int).name() << std::endl;
    delete anum;
    return 0;
}
Here we have at least two overloaded operators and what appears to be a function (though it can be another operator similar to sizeof) taking on int, a type, as an argument. I've been wondering if it is possible to take on types as arguments in functions, since this would prove rather useful in specific circumstances. Any help is appreciated.
Wow I changed my sig!

#11
ZekeDragon

ZekeDragon

    Writes binary right handed and hex left handed

  • Moderators
  • 2,103 posts
Question 6:
How do I throw a standard exception?


This question is pretty straightforward, and I haven't found an answer anywhere on the net for it. Here's what I want to know, how do you throw the exceptions that are found in the <stdexcept> include file? For example:
try {
    if (some_bad_condition() = true) {
        throw std::bad_alloc();
    }
} catch (std::bad_alloc&) {
    std::cout << "The allocation failed." << std::endl;
    exit(EXIT_FAILURE);
}
I know how to throw my own exceptions, that isn't hard in the slightest. The problem stems from the fact that when I try to use std exceptions as my own, they don't work at all. Is there something simple I'm missing, or is this intentionally prevented by the library?

Edited by ZekeDragon, 05 September 2009 - 07:07 PM.

Wow I changed my sig!

#12
ZekeDragon

ZekeDragon

    Writes binary right handed and hex left handed

  • Moderators
  • 2,103 posts
FOLLOW UP: I answered my own Question 5 and Question 6. In the first situation, no, I can't send a type as an argument. Instead, however, I can send a typeid().name, which works almost as well since all I've got to do is interpret the name that comes out using comparisons between the input and various accepted typeid's. In the second, you can throw a standard exception, you just have to not be a complete newb like I was and actually give the constructor the arguments it wants, I didn't read the doc's thoroughly enough. :P I was using out_of_range as a test exception, and that requires a std::string as an argument, and I wasn't providing one. Well, anyway, I don't have another question yet, but my Question 1 is still standing, I'm not exactly sure how one would instantiate an incomplete class type. I may be familiar with it programatically, just don't know that that's what it's called. O_o
Wow I changed my sig!