What's a good way of declaring instance variables in C++ without constructing them?

12,086

Solution 1

This is very bad habit (replaced classes with objects):

I'm used to declaring objects in a large scope, typically near the top of the file, outside any functions, and then constructing them when in use.

Forget about it. Define object when you need it.

int main() { 
     A a;
     ...a...
     A b;
     ...b...
 }

This is C++ thinking.

I believe in C# it is bad habit too. What if you use object without defining it - you'll get null reference exception - why play with such dangerous thing.

BTW, C++ equivalent to C# object is shared_ptr:

std::shared_ptr<A> a;
int main() {
   a = std::make_shared<A>(...);
   // do not need to call delete
}

In C++ you can also use std::unique_ptr if you do not need an object to be shared.

But don't do this, don't use global variables...

Solution 2

That's because in C# a class is a heap based, referenced object. So:

C#

MyClass a; //null reference
a = new MyClass (param1, param2);

However:

C++

MyClass a; //Myclass is constructed on stack

The C# version is equivalent to this in C++:

Myclass* a = 0; //null reference
a = new Myclass (param1, param2);

A class can live on the stack in C++, whereas it cannot in C#.

C# provides a struct value type that can live on the stack:

MyStruct a; //lives on the stack

However I can provide a struct constructor with arguments:

struct MyStruct
{
   public MyStruct (int a, int b) { /*assign to members vars*/ }

   int A, B;
}

C# code:

MyStruct a; // this creates a on stack and zero initializes int A and B

This:

MyStruct a; //does same as above

a = new Mystruct(1, 2); //a.A = 1, a.B = 2

Solution 3

  1. Staying in a smaller, more local scope

This is what you should do, and not only in C++, but in all languages in as much as they allow you to.

but I can imagine a function needing a lot of external classes having a massive amount of needed arguments.

Functions should take the arguments they need to take. If you feel there are too many, maybe you should refactor the function into other pieces. The number of arguments is just one more measure of the complexity of your function. Accessing global objects not only does not simplify the function, but it makes it much harder to identify where the global objects are used/accessed/modified and that in turn makes your code much more complex to maintain.

If your function takes many arguments, either it is not really a function but a complex mess of different operations, or else the arguments probably group in some meaningful entities. In the latter case, just create types that represent those entities and you will end up passing a few arguments.

On a side note, I am not sure that in C# you really do what you say you are doing... in particular, most code in C# is inside classes, so most probably what you are used to doing is having classes with members declared at the top and used everywhere else. If that is the case, you can apply the same paradigm in C++. Create classes, with how many members you need.

Solution 4

It sounds like you want to overload the constructor.

Solution 5

Use static variables in a function to delay the creation of the object until needed.

Foo & getInstance () {
    static Foo foo(arg1, arg2, ...);
    return foo;
}

void main () {
    Foo & x = getInstance();
}

If you need getInstance to create a dynamic object (as in getInstance(x, y, z)), but only want to pass in the aguments once, you can do:

struct FooFactory {
    int arg1;
    float arg2;
    Bar arg3;

    bool valid;

    FooFactory ();

    Foo & getInstance ();
};

FooFactory::FooFactory () : valid(false) {}

Foo & FooFactory::getInstance () {
    if (!valid) throw Error();
    static Foo foo(arg1, arg2, arg3);
    return foo;
}

FooFactor factory;

void main () {
    factory.arg1 = ...;
    factory.arg2 = ...;
    factory.arg3 = ...;
    factory.valid = true;
    Foo & x = factory.getInstance();
}

Of course this is the basics. I'm not bothering with information hiding or anything like that. And you can avoid factory.getInstance() by using operator() () instead of getInstance () and renaming factory to getInstance. I'm also not saying this a good idea or not. I'm just showing a way how the OP's question can literally be done.

Share:
12,086
seandewar5
Author by

seandewar5

Hey there! It's been a long while since I've asked questions on this website; nevertheless, I hope you find them and their answers useful!

Updated on June 18, 2022

Comments

  • seandewar5
    seandewar5 about 2 years

    I've been searching around the internet a lot for this topic, and I haven't really reached a firm answer. As mainly a C# programmer, I'm used to declaring classes in a large scope, typically near the top of the file, outside any functions, and then constructing them when in use.

    After moving on to C++, the only way of replicating this is to have a default constructor, which is fine, but in some cases, I'd rather have a constructor which requires arguments than an argument-less default constructor.

    After searching around the internet for a solution, I've come across a few pieces of advice, which have their flaws:

    1. Pointers

    Some people have suggested to have a dynamic pointer in the desired scope, and then assign the pointer to point at the location of the class when constructed.

    CClass* pClass = 0;
    
    int main()
    {
        pClass = new CClass(1337);
    
        delete pClass;
        return 0;
    }
    

    The problem with this approach is that you have to remember to delete the pointer afterwards, thus, static pointers are much more "safe". Also, I'm guessing there will be slight memory overhead doing this, albeit not much, due to having the pointer.

    2. Have a default construct anyway

    It is sometimes recommended to have a default constructor anyway, which just zeros everything inside the class:

    class CClass
    {
    public:
        CClass() : leetNumber(0) {}
        CClass(int leetNumber) : leetNumber(leetNumber) {}
    private:
        int leetNumber;
    };
    
    //Defaults leetNumber to 0 through default ctor
    CClass myClass;
    
    int main()
    {
        myClass = CClass(1337);
    
        return 0;
    }
    

    But what happens if you can't just zero everything inside the class? What if you have another class in there which cannot just be initialized to nothing? What will you do if the user tries to access a function inside the class without proper initialization of the members? (You could check for this, but I believe it would take too much code, especially if you have a lot of members).

    3. Staying in a smaller, more local scope

    There have been suggestions where people have said to stay in a small scope, pass the class as a reference to other functions that may need it, and construct as soon as they declare the class:

    class CClass
    {
    public:
        CClass(int leetNumber) : leetNumber(leetNumber) {}
        int getLeetNumber() { return leetNumber; }
    private:
        int leetNumber;
    };
    
    bool GetMuchNeededAmazingNumberFromClass(CClass& myClass)
    {
        if(myClass.getLeetNumber() == 1337)
            return true;
    
        return false;
    }
    
    int main()
    {
        CClass myClass = CClass(1337);
        if(!GetMuchNeededAmazingNumberFromClass(&myClass);
            return 1;
    
        return 0;
    }
    

    This is good in the sense that you can see what function needs what, but I can imagine a function needing a lot of external classes having a massive amount of needed arguments.

    There are many more examples, but I cannot seem to find one that I can rely on, especially coming from a C# background, where this stuff is nice and easy.

    Thanks.

    EDIT:

    Let me elaborate more on what I'm asking for - in C#, you can do the following:

    public class Program
    {
        //See how I'm able to do this, without calling the ctor.
        static AmazingClass amazing;
    
        public static void Main()
        {
            //And then call the constructor when I want.
            amazing = new AmazingClass(1337);
        }
    }
    

    This allows me to create the class without actually constructing it, this is what I'm looking for in C++.

    Thanks again.