1

So to summarize initialization in C++ , constructor can

  1. initialize primitive types to garbage values in memory
  2. call default constructors on members automatically
  3. initialize member objects with nontrivial constructors in init list

but it cannot init array properly. Ref: C++: constructor initializer for arrays

My question is: how can I do meaningful initialization in the function body? Before entering the braces, everything has already been initialized in one way or another. The braces only contain cleaning up work, things like cout<<"This object is constructed!" . Ref: Use of constructor in C++

What if I want to preprocess parameter before passing it to the constructor of member object? For example:

class Foo{
public:
    int x;
    Foo(int y){
        x = y;
    }
};

class Bar{
public:
    Foo foo1;
    Bar(int y){
        int z = superComplicatedOperation(y);
        // and all other heavylifting work
        foo1 = A(z);
    }
};

The code above does not compile because compiler tries to init foo1 before entering the braces in constructor.

I have seen suggestions on solving this problem by providing a default constructor to Foo. So the constructor of Bar will first init foo1 to garbage value, and then assign foo1 to something meaningful in the ctor body. This is also the solution to array initialization in the quoted thread. But isn't "initializing to garbage value" an oxymoron? If the compiler wants me to do that, then it wouldn't complain about having no default ctor.

In a word, ctor is supposed to init the object in a user defined way, but I don't see any way to do a nontrivial init. Am I missing something here?

================================================================================

Clarifications: I am askingI am well aware of the Bar(int y): foo(superComplicatedOperation(y)) syntax. My question is on how to do it nicely. From a design point of view, I believe that codes in superComplicatedOperation() really should belong to the ctors. For example, say Bar takes 2 parameters (int x,int y) to initialize. We can imagine they are xy coordinates on plane. Foo also takes coordinates, but with a different reference frame. Then I need

Bar(int x,int y):foo(f1(a,b),f2(a,b))

And this kind of things quickly get ugly if ctor has a handful of params. Besides, init list looks crowded.

And if ctor body "touches" member objects, it means init list does not set members to a desired state, but rather some default zero-like states(e.g. empty vector). Therefore when calling ctor, first memory for the Bar is allocated, then members objects(subchunks of memory) go through the phases:

garbage value/uninitialized
--- init list or default member ctor ---> default "zero-like" state
--- ctor body --------------------------> actually desired initial state

So initialization is supposed to be a two step process?

Community
  • 1
  • 1
  • 1
    You could do `Bar(int y) : foo1(A(superComplicatedOperation(y)) { }` to avoid the "garbage" initialization, although I wouldn't recommend it. I would instead prefer doing `superComplicatedOperation(y)` *prior* to calling the constructor for `Bar` in the first place, and then passing the result to the constructor as a parameter. – Turix Jun 28 '14 at 03:48
  • "but it cannot init array properly.", what do you mean by that. the question you're linking to is pretty old. since then we've got c++11 – Cheers and hth. - Alf Jun 28 '14 at 03:54
  • @Cheersandhth.-Alf: Don't be obtuse; you know precisely what that means. In C++03 there is no way to initialise an array member. – Lightness Races in Orbit Jun 28 '14 at 03:57
  • @LightnessRacesinOrbit: re "In C++03 there is no way to initialise an array member", that's incorrect. in C++03 you can value-initialize an array member directly in the constructor memory initializer list. for more complex array initialization via C++03 memory initializer list you just wrap the array in a struct, or use a logical array class such as std::vector. or else, initialize in constructor body. not a technical problem, but one of convenience (which is very far from "no way"). – Cheers and hth. - Alf Jun 28 '14 at 04:00
  • There is a difference between "initializing to garbage value" and initializing to empty, default value or null. – Chris Drew Jun 28 '14 at 04:54

5 Answers5

2

Preprocessing a parameter is fairly trivial:

class Bar
{
    Foo foo;
    Bar(int y) : foo(superComplicatedOperation(y)) {}
}

or you can use a C++11 delegating constructor:

class Bar
{
    Foo foo;
    Bar (SuperComplicatedData z) : foo(z) {}
    Bar (int y) : Bar(superComplicatedOperation(y)) {}
}

What can you do in a constructor function body? Lots of things: fill containers, configure member objects, set up member object relationships, etc.

Don Reba
  • 13,814
  • 3
  • 48
  • 61
1

[...] constructor can

  1. initialize primitive types to garbage values in memory
  2. call default constructors on members automatically

This is default-initialization, which applies to every member that's not specified in the initializer list and do not have an initializer in the class definition.

  1. initialize member objects with nontrivial constructors in init list

The member initializer list can also initialize primitive members.

but it cannot init array properly. Ref: C++: constructor initializer for arrays

One of the answers there noted that this can now be done in C++11.

My question is: how can I do meaningful initialization in the function body? Before entering the braces, everything has already been initialized in one way or another. The braces only contain cleaning up work, things like cout<<"This object is constructed!" . Ref: Use of constructor in C++

Um...no. Frequently there's substantial logic in the constructor body proper. It's definitely not only for "cleaning up". It is true that at the time you enter the constructor body every member is initialized in some way (which may include "not initialized at all" for primitive types), but nothing says you can't assign to them or otherwise modify them. Sometimes you have to if there's some sort of circular dependencies. Sometimes the code to compute the stored value is long enough that it's far more readable inside the constructor body.

The code above does not compile because compiler tries to init foo1 before entering the braces in constructor.

I have seen suggestions on solving this problem by providing a default constructor to Foo.

Well, you can do : foo1(A(superComplicatedOperation(y)) in your example.

So the constructor of Bar will first init foo1 to garbage value, and then assign foo1 to something meaningful in the ctor body. This is also the solution to array initialization in the quoted thread. But isn't "initializing to garbage value" an oxymoron? If the compiler wants me to do that, then it wouldn't complain about having no default ctor.

A default ctor means that a object constructed with it is a valid object. A default ctor doesn't necessarily leave members uninitialized (or "initialized to garbage values"). Consider the default ctor for a std::vector, it had better set its internal state to something that represents an empty vector!

More fundamentally, the job of constructors (default or not) is to establish the object's invariants upon which the functions that operate on the object depend. Some objects may have no invariants, but many do (for instance, vector's internal pointer had better be either a valid pointer or null). If you have no default constructor, the compiler has no way of creating a valid object when no argument is supplied; it can't just give you something "initialized to garbage value", because it has no way of knowing whether that can in fact be a valid object of that type.

Community
  • 1
  • 1
T.C.
  • 133,968
  • 17
  • 288
  • 421
0

You can use an initializer list, as you should for any member variables you need to initialize.

You may prefer to initialize things to sane values and then have a function that can set the values later. It's not as clean, but if you plan to construct many objects and you can precompute the value for many of them at once it may save you some time rather than calculating the values for each object.

#include <iostream>

int superComplicatedOperation(int a)
{
    return a * 10;
}

class Foo
{
    public:
    int x;
    Foo(int y)
        : x(y)
    {
    }
};

class Bar
{
    public:
    Foo foo1;
    Bar(int y)
        : foo1(superComplicatedOperation(y))
    {
    }
};

int main()
{
    Bar b(7);
    std::cout << b.foo1.x << "\n";
    return 0;
}
Retired Ninja
  • 4,785
  • 3
  • 25
  • 35
0
class Bar {
public:
    Foo foo1;
    Bar(int y) : foo1(superComplicatedOperation(y)) { }
};

You can use the Constructor's initialization list for this work. It passes the values to the Ctors of the members for the first creation, this prevents the creation of a garbage object.

Schamote
  • 47
  • 7
0

I guess I don't understand. I wrote code in the 1990s which has a constructor with hundreds of lines of code which does all kinds of things: opens files, instantiates dozens of objects which create linked lists and arrays based on processing file data. It certainly can initialize an array properly.

wallyk
  • 56,922
  • 16
  • 83
  • 148