9

This compiles, but I've never seen it in any other code. Is it safe?

Testclass():sources(new int[32]){}

Instead of:

Testclass(){
    sources = new int[32];
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
johnbakers
  • 24,158
  • 24
  • 130
  • 258
  • 2
    What's the difference between a pointer being initialized with `new` and any other member being initialized otherwise? – chris Feb 04 '13 at 06:55
  • @SebbyJohanns better way is to use vector, which is `"safer"` – billz Feb 04 '13 at 07:01

2 Answers2

15

Use:

Testclass():sources(new int[32]){}

This is using member-initialization-list which is the preferred way to initialize members.

By "safe" or "okay" you probably meant, whether it is exception-safe? What if new throws the bad_alloc exception?

Well, in that case, the destructor will not be called, because the object is not fully-constructed, as constructor-body is not executed. There may be a resource leak if you've acquired any resource in the initialization list.

Consider this,

class X
{
    int  *ints;  // Order of declaration matters!
    Huge *huges; // It ensures that huges will be initialized after ints

    X() : ints(new int[32]), huges(new Huge[10000]) {}
};

If new Huge[10000] throws an exception, the memory allocated toints will leak!

In such cases, function-try-block can be useful. See these:

If you think about this problem of exception-safety, you will soon realize that if a class manages just one resource, then the life will be easier. If a single class manages more than one resource, then you wouldn't be able to decide which one threw an exception in the member-initialization-list, and consequently, you wouldn't be able to decide which one is to be deallocated in the catch block of function-try-block. A resource leak is destined.

However, if a class needs more than one resource, then first you encapsulate each of the different type of resource in a class, and declare objects of these resource-managing class as a member of your class.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I know that it is. The question is, using `new` in this list is okay? – johnbakers Feb 04 '13 at 06:56
  • @SebbyJohanns: *"okay"* in what sense? – Nawaz Feb 04 '13 at 06:57
  • Well I have read that it is wise practice to initialize your ivars as much as possible in the member initializer, yet in all the code I see, pointers using `new` are defined only in the body of the constructor, not in this list, so I wondered if there was a good reason for this, i.e. is doing it "okay" (safe, etc) – johnbakers Feb 04 '13 at 06:58
  • 1
    @SebbyJohanns: Well, by "safe" or "okay" you probably meant, whether it is exception-safe? What if `new` throws bad_alloc? Well, in that case, the destructor will not be called. – Nawaz Feb 04 '13 at 07:00
  • 2
    Ah, function-try-blocks. I've never had to use one, but I can imagine the confusion that comes out of those who haven't had the luxury of discovering them in a "Hidden Features of C++" question on SO :) – chris Feb 04 '13 at 07:10
  • `a class must not manage more than ONE resource` ?!?!? One of the great benefits of aggregates is precisely this - combining the management of more than one resource into a single object. – Lightness Races in Orbit Feb 04 '13 at 07:38
  • @SebbyJohanns: Real code doesn't use raw pointers anyway – Lightness Races in Orbit Feb 04 '13 at 07:39
  • @LightnessRacesinOrbit: Isn't it better if the aggregate combines more than one resource-managing objects instead of resource themselves? – Nawaz Feb 04 '13 at 07:44
  • @Nawaz: Oh that, well, yes I suppose. Maybe. Sometimes not, if you need to be conservative about overhead and the two resources are to be managed in concert. There are also arguments about nesting too many damn layers for no real reason – Lightness Races in Orbit Feb 04 '13 at 07:51
  • @LightnessRacesinOrbit: I agree. But that is rare. I rephrased my statement, so the tone is suggestive now. – Nawaz Feb 04 '13 at 07:52
  • note: the example is wrong: huges is allocated before ints (initialisers are parsed inversely, but for base class initialiser). In any case I think we get the point – Giacomo Catenazzi Jun 30 '23 at 15:54
  • @GiacomoCatenazzi: *"huges is allocated before ints"*.. how? the member data *huges* is declared _after_ the member data *ints*? So how *huges* gets allocated before *ints*? Does the spec support your argument? I dont recall the relevant text (it's been 10 years since I saw such stuff). – Nawaz Jun 30 '23 at 17:34
  • 1
    @Nawaz: right. I was interpreting wrongly some part of C++ (using examples with mess up things, intentionally, but I interpreted wrongly the reason). So I was wrong. Declaration order matter (and in the right order). (The constructor list order in constructor doesn't matter, and the destructor is called in inverse order). – Giacomo Catenazzi Jul 05 '23 at 07:29
0

It's okay if you use the weird C++ syntax for using a try/catch block in a constructor's initializer list:

class C {
    int *n;
public:
    C();
};

C::C()
try : n(new int[100])
{
  // do stuff here
}
catch(...)
{
  // handle exceptions
}

int main()
{
    C c; // Should be safe...
}
Moonshine
  • 19
  • 4
  • What if there are more than one pointer members which are to be allocated in the mem-init-list? What would you handle in the `catch` block? and how? – Nawaz Feb 04 '13 at 10:01
  • I don't know of a way to test which object failed to allocate in that case. If you need that much control, as far as I know you're pretty much stuck writing an init() which can test each allocation individually for exceptions. I suppose if you HAD to use the initializer list, you could make wrapper classes for all the members you're allocating and make them throw individual, custom exceptions.. then test for each one. That'd be just getting ridiculous though. – Moonshine Feb 05 '13 at 21:49