0

Consider this code:

#include <iostream>
using namespace std;

struct SOME_OTHER_BASE {
    // members
};

struct FOO : SOME_OTHER_BASE {
    int value;
    static FOO *CreatePtr(int param) 
    { 
        FOO *res = new FOO(); 
        res->value = param; 
        return res;
    }
    static FOO &CreateRef(int param)
    {
        return *CreatePtr(param);
    }
};

int main() {
    FOO *ptr = FOO::CreatePtr(2);
    FOO &ref = FOO::CreateRef(4);
    FOO local = FOO::CreateRef(5);

    cout << "ptr: " << ptr->value << endl;
    cout << "ref: " << ref.value << endl;
    cout << "local: " << local.value << endl;

    delete ptr;
    delete &ref;
    return 0;

Requirements:

  1. FOO::CreatePtr method may return NULL under some circumstances (allocation failed and was handled, invalid parameters, etc.)
  2. FOO is POD, but not an aggregate

What I want: as few as possible delete calls (and memory leaks).

My observations:

  1. I can use FOO::CreatePtr, check for NULL and always delete afterwards
  2. I can use FOO::CreateRef, but then I can't really check for null and I still have to delete
    1. I could make FOO::CreateRef return a special instance when CreatePtr was NULL. That would help with NULL checking, but still not with deleting.
  3. I could use the local variant, but I think I have a memory leak right there (the CreatePtr(5) never gets deleted, only copied into local)
    1. This is highly ineffective because there's an unnecessary alloc and a copy
    2. I can't use brace initialization because FOO is not an aggregate

Is there any way I could declare a local variable of FOO type and initialize it within declaration such that it would automatically get deleted on function exit?

velis
  • 8,747
  • 4
  • 44
  • 64
  • 5
    "What I want: as few as possible delete calls (and memory leaks)." Simple, don't call `new` in the first place. – Emil Laine Mar 31 '15 at 21:34
  • 3
    with out more context to understand why, I wonder why not just use smart pointers and RAII – DaveB Mar 31 '15 at 21:36
  • 1
    My eyes hurt from looking at that code. UPPERCASE identifiers. Why? – Martin Ba Mar 31 '15 at 21:40
  • 3
    @zenith although not mandated it's common practice to reserve all caps for constants and #defines, it's confusing to read code that ignores such a common guideline – DaveB Mar 31 '15 at 21:44
  • 1
    @zenith - because noone does it except for macro names. I dont want no source code screaming at me. Nuff said. – Martin Ba Mar 31 '15 at 21:44
  • See [this question](http://stackoverflow.com/questions/17061955/what-should-the-return-type-be-when-the-function-might-not-have-a-value-to-retur) for possible approaches to this problem. IOW, you don't have to use `new`, especially if your type isn't polymorphic. – milleniumbug Mar 31 '15 at 22:12
  • It's just a sample. Don't know why I used caps. Normally my classes are CamelCase. – velis Mar 31 '15 at 22:33
  • @DaveB: RAII seems out of reach due to POD / non-aggregate requirement. Smart pointers seem a bit of an overkill. Will use them if they are my only option. – velis Mar 31 '15 at 22:38
  • According to http://stackoverflow.com/questions/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special/4178176#4178176, PODs are aggregates, which kind of makes your requirement that "FOO is a POD, but not an aggregate" kind of silly. Can you clarify this? – milleniumbug Mar 31 '15 at 22:58
  • Of course: since FOO derives from SOME_OTHER_BASE, it is no longer an aggregate. It remains a POD though. Basically I'm trying to add more methods to a library class without touching the class's data structure because I want to pass derived class to functions dealing with original - SOME_OTHER_BASE. Also @DaveB: this also eliminates smart pointers because I can't use them as parameters to functions requiring base class. Smart pointer is its own class even though it tries to mask this fact. – velis Mar 31 '15 at 23:04
  • While that is an excellent answer, I'm fairly certain that my non-aggregate remains a POD. Some other questions dealing with the subject exposed the difference between a POD and an aggregate. Also, a quick read indicates that the answer below the one you linked does not mention the requirement that a POD should be an aggregate. This is not an issue though: if I introduce a constructor, I get a VMT (an lose POD). If I don't have a constructor, I can't easily construct the instance. – velis Mar 31 '15 at 23:19
  • Fair enough, I admit my mistake - I wasn't familiar with the new rules on PODs. It seems that (limited) inheritance is allowed for PODs in C++11. – milleniumbug Mar 31 '15 at 23:36
  • Not a problem. Your answer solved the problem in this question, though I was still left with a huge issue of dynamic allocation. Just talking about it made me realize how I can solve this. Thanks. – velis Mar 31 '15 at 23:38

2 Answers2

2

If your object can't be created, throw an exception. It's more useful anyway, as it gives more context why an object couldn't be created and allows the program to fix the problem. Also you avoid the problems with delete because you don't use new. If the type was to be used polymorphically, you would need to use smart pointers like std::unique_ptr or std::shared_ptr.

#include <iostream>
#include <stdexcept>
using namespace std;

struct SOME_OTHER_BASE {
    // members
};

struct CreationFailure : std::runtime_error
{
    CreationFailure(const std::string& s) :
        std::runtime_error(s)
    {

    }
};

struct FOO : SOME_OTHER_BASE {
    int value;

    FOO(int value) : 
        SOME_OTHER_BASE(), // pass the constructor arguments
        value(value)
    {
        // additional actions
        if(/* creation failed */)
            throw CreationFailure("out of memory");
    }
};

int main() {
    FOO local(2);

    cout << local.value << endl;
    return 0;
}

If you can't add constructors, the same logic can be moved to the factory function:

FOO createFoo(int v)
{
    FOO f;
    f.value = v;
    if(/* creation failed */)
        throw CreationFailure("out of memory");
    return f;
}
milleniumbug
  • 15,379
  • 3
  • 47
  • 71
  • There was a requirement that FOO is a POD. If I could have the constructor, this would not be an issue. – velis Mar 31 '15 at 22:45
  • @velis Oh, I missed that requirement. But I think the same logic can be implemented in your factory function. I'll update it in a minute. – milleniumbug Mar 31 '15 at 22:55
  • You are correct, somehow I forgot that I can return whole objects :( This does a full copy on assignment, though it doesn't introduce the leak I had with local variant. – velis Mar 31 '15 at 23:14
  • Also, I just realised that I oversimplified my example :( I'm using allocation because my POD is of varying size (contains a char[] at the end). I guess there's no way I can tell the compiler how big my class is if I'm playing with dynamic allocation. – velis Mar 31 '15 at 23:27
  • You're using flexible array member in C++? That's only defined in C, so you're using a language extension then. – milleniumbug Mar 31 '15 at 23:38
  • Well, I'm just afraid to admit what I'm actually doing :p Not many driver samples around using classes. But I need inheritance and polymorphism, so I figured this would be the best approach. I could do it with a gazillion functions, but they're so clumsy. – velis Apr 01 '15 at 00:06
0

Not sure if you should use pointers at all for your case.
Moreover, you're not deleting the pointer created to your local variable.

Since FOO should be a POD and depending on its size (which is dependent on SOME_OTHER_BASE's size), you could return it by value from your create method:

struct FOO : SOME_OTHER_BASE {
    int value;
    static FOO Create(int param) {
        FOO foo;
        foo.value = param;
        return foo;
    }
};

int main() {
    FOO local = FOO::Create(2);
    FOO &ref = local;
    FOO *ptr = new FOO(FOO::Create(6));
    // can check if ptr is NULL if you want

    cout << "local: " << local.value << endl;
    cout << "ref: " << ref.value << endl;
    cout << "ptr: " << ptr->value << endl;

    delete ptr;
    return 0;
}
ArnonZ
  • 3,822
  • 4
  • 32
  • 42