0

There was an article i found long ago (i cant find it ATM) which states reasons why the new keyword in C++ is bad. I cant remember all of the reasons but the two i remember most is you must match new with delete, new[] with delete[] and you cannot use #define with new as you could with malloc.

I am designing a language so i like to ask how would you change the C++ language so new is more friendly. Feel free to state problems with new and articles. I wish i can find the article link but i remember it was long and was written by a professor at (IIRC) a known school.

8 Answers8

6

I cannot see any reason to replace the new keyword with something else (and seems to be that C++ committee agree with me). It is clear and makes what it should. You could override operator new in your class, no need to use defines.

To eliminate new[]/delete[] problem you could use std::vector.

If you want to use smart pointer you could use it, but I want to control when smart pointer will be used. That's why I like how it works in C++ — high level behavior with ability to control low level details.

Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • 6
    Agreed. Instead I'd replace the new keyword in Java, since you can't put an object on the stack anyway! – Drew Hall Aug 13 '09 at 07:27
  • Note that `boost/tr1/std::array` is not/will not be a replacement for `new[]`/`delete[]`. (That would be `std::vector`.) As for your argument: On the one hand, I can see the advantage of having the RTE taking care of `delete` vs. `delete[]`. OTOH, C++ offers a lot of alternatives (as `std::vector`) to avoid dynamic allocation altogether. But, as I've said in http://stackoverflow.com/questions/1245430/over-the-last-7-8-years-what-are-the-biggest-influences-on-c-programming/1246870#1246870, that's mainly a question of how C++ is taught, not what it offers. – sbi Aug 13 '09 at 09:33
  • tr1::array is a replacement for new[]/delete[]. It could allocate array on the stack. std::vector always allocates memory on the heap. – Kirill V. Lyadvinsky Aug 13 '09 at 10:05
  • No, Jla3ep, `array` is not a replacement for `new[]`. If you're using `new[]`, then it's because you don't know the number of elements until run time. But `array` has a non-type template parameter indicating its size, and such a parameter must be a compile-time constant. If you know the required number of elements at compile time, then you never should have been dynamic allocation in the first place. `array` is a replacement for fixed-size arrays, not those allocated with `new[]`. For that, use `vector`. – Rob Kennedy Aug 13 '09 at 15:51
  • Oh, yes... from that side :) Fixed my answer. – Kirill V. Lyadvinsky Aug 13 '09 at 16:13
3

Problem match new, delete, new[], delete[]

Not really a big deal.
You should be wrapping memory allocation inside a class so this does not really affect normal users. A single obejct can be wrapped with a smart pointer. While an array can be represented by std::Vector<>

cannot use #define with new as you could with malloc.

The reason to mess with malloc like this was to introduce your own memory management layer between your app and the standard memory management layer. This is because in C you were not allowed to write your own version of malloc. In C++ it is quite legal to write your own version of the new which makes this trick unnecessary.

Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562
2

I'd give it the semantics of new in C# (more or less):

  1. Allocates memory for the object.
  2. Initializes the memory by setting the member variables to their default values (generally 0 for values, null for references).
  3. Initializes the object's dynamic binding mechanism (vtables in C++, type def tables for managed VMs).
  4. Calls the constructor, at which point virtual calls work as expected.
  5. For a language without garbage collection (eww for a new language at this point), return a smart_ptr or similar from the call.

Also, make all objects either value types or reference types, so you don't have to keep an explicit smart_ptr. Only allow new to heap-allocate for reference types, and make sure it contains information to properly call the destructor. For value types, new calls the constructor on memory from the stack.

Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • I dunno, I like that way D does it. Garbage collection, but the ability to do RAII. – GManNickG Aug 13 '09 at 07:09
  • Between `try`/`finally` and `using`, I never feel like I'm missing anything. – Sam Harwell Aug 13 '09 at 07:16
  • That presumes that all value types are "small" (i.e. fit on the stack). I would submit that huge matrices are value types but certainly don't belong on the stack. – Drew Hall Aug 13 '09 at 07:18
  • @Drew: Not really the thread for details, but I'm actually worked on some interesting (to me at least) cooperation between matrices and the GC/JIT to meet C/C++ speeds in a managed VM for scientific computing. If I wasn't tied down here for school, it's probably the area I'd be trying to get a job. :D – Sam Harwell Aug 13 '09 at 07:26
  • Me, I'm missing `using` for composed subobjects (i.e. for member fields). – Pavel Minaev Aug 13 '09 at 09:05
  • @Pavel, that's never a problem because every class with `IDisposable` fields implements `IDisposable`. – Sam Harwell Aug 13 '09 at 21:52
  • You miss the point - when I _write_ a class with `IDisposaable` fields, I have to implement `IDisposable` myself. I do not want to do that - I just want to say "these here fields `Foo` and `Bar` are owned by me, so make me `IDisposable`, and when someone calls `Dispose` on me, delegate to them", complete with support for the usual C# `IDisposable` pattern (with protected method with `bool` etc). In other words, precisely how C++/CLI does that already. – Pavel Minaev Aug 14 '09 at 04:35
  • Clarification of my earlier comment: Huge matrix OBJECTS can/should be on the stack, but their DATA clearly should not be. That was bothering me... – Drew Hall Aug 17 '09 at 03:15
1

Use Garbage Collection so that you never need to match new with anything.

djna
  • 54,992
  • 14
  • 74
  • 117
  • 1
    Agree. C++ uses a very versatile garbage collection mechanism called smart pointers. – Martin York Aug 13 '09 at 07:24
  • Hear, hear! RAII is waaaaayyyy more powerful than non-deterministic GC! – Drew Hall Aug 13 '09 at 07:25
  • 1
    *But* smart pointers are enormously slower than modern garbage collectors. – Sam Harwell Aug 13 '09 at 07:30
  • And smart pointers don't do the same job - freeing cycles - and usually require locking ( at at least the bus level ) for each acquire or release, rather than the coarse locking required by gc. – Pete Kirkham Aug 13 '09 at 08:50
  • it's a valid point. I like RAII as much as anyone, but the claim that it completely replaces a GC is just silly. A GC would be more efficient, and would handly cycles. The downside is that you lose deterministic destruction. I don't see why a language shouldn't be able to offer both. Let me choose, when defining a type, how it should be destructed - GC or deterministic. – jalf Aug 13 '09 at 09:42
  • 2
    I've got a proof - that won't fit in the margin here - showing GCs are necessarily no more efficient than RAII, because for every GC implementation there is a trivial RAII implementation on top of that. – MSalters Aug 13 '09 at 11:07
  • Why the downvote? Really helps if you tell. GC may or not be the best, but is it intrinsically bad? – djna Aug 13 '09 at 12:36
1

By using the STL container classes and the various boost:smart_ptrs, there's little need to ever explicitly call new or delete in your C++ code.

The few places you might need to call new (e.g, to initialize a smart pointer) use the Named Constructor Idiom to return your class type pointer wrapped in, e.g., a boost:shared_ptr.

But C++ and the STL work very very hard to allow you to treat most objects as value objects, so you can construct objects rather than pointers and just use them.

Given all this, there's little need to replace the new operator -- and doing so would introduce a host of problems, whether by requiring a garbage collector, or by reducing the fine low-level control C++ offers programmers.

tpdi
  • 34,554
  • 11
  • 80
  • 120
0

If your new language is garbage collected, you can avoid the new keyword. Thats what Python did (and Lisp did almost 5 decades ago!). Also see an answer provided by Peter Norvig for a similar question here. (Is no "news" good news?)

Vijay Mathew
  • 26,737
  • 4
  • 62
  • 93
  • In common lisp, the equivalent of 'new' is 'make-instance', so it's a function rather than a keyword/special form, but otherwise similar ( taking the class of the object and its initialisation parameters ). – Pete Kirkham Aug 13 '09 at 08:52
  • make-instance is part of CLOS, which in turn is implemented in Lisp itself. (SICP and "The Art of the Metaobject Protocol" will throw more light on this). Note that make-instance does not make any VM level calls to create a new instance of an object. In fact, the Lisp VM is not aware of the existence of classes and objects. So this cannot be compared with the implementation strategies followed by Java, etc. – Vijay Mathew Aug 13 '09 at 09:21
  • SCIP illustrates an object system in scheme as a teaching aid. Professiotnal Common Lisp implementations such as Allegro implement a 'native' CLOS rather than using conses for large performance gains. – Pete Kirkham Aug 13 '09 at 19:26
  • Agreed. BTW, how is CLOS implemented? It is implemented in Lisp itself. In the end, objects turn out to be procedures with state. This is simply illustrated in SICP and detailed in "The Art of the Metaobject Protocol". The point is, make-instance should not be confused with new. – Vijay Mathew Aug 14 '09 at 04:59
0

Sometimes you want to replace the constructor with a factory. This is a well known refactoring. Replace Constructor With Factory Method. So perhaps this is what the article meant?

Incidentally you will often see straight calls to new being replaced with a Factory Method.

DI frameworks such as Unity take this concept to another level. As you can see in the following C# code, there is no "new" applied to create the IMyClass interface:

IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType<IMyClass, SomeClass>();
IMyClass thing = myContainer.Resolve<IMyClass>();
RichardOD
  • 28,883
  • 9
  • 61
  • 81
  • On one of my experimental C++ projects, I actually wrote my class declarations in XML and generated headers for them, where the constructors were protected but exposed a static Create method that gave them well-behaved semantics and return smart pointers. :) – Sam Harwell Aug 13 '09 at 07:35
0

The reason that C++ has a separate new operator ( or C malloc ) is primarily so that objects can be created whose lifetimes exceed the scope of the function which creates them.

If you had tail call elimination and continuations, you wouldn't care - the objects could all be created on the stack and have unlimited extent - an object can exist until you call the continuation that corresponds to the object going out of scope and being destructed. You might then need something to garbage collect or otherwise compress the stack so it doesn't become full of no-longer required objects ( Chicken Scheme and TinyOS 2 are two different examples for giving the effect of dynamic memory without dynamic memory at either runtime or compile time; Chicken Scheme doesn't allow for RAII and TinyOS doesn't allow for true dynamic allocation ), though for a large amount of code such a scheme wouldn't be vastly different to RAII with the facility to chose to change the order the objects are destructed.

Pete Kirkham
  • 48,893
  • 5
  • 92
  • 171