Click here to Skip to main content
Click here to Skip to main content
 

new(local) for safer and simpler code with no need for smart-pointer template wrappers

, 3 Oct 2012
Rate this:
Please Sign up or sign in to vote.
reduce bugs and memory leaks by using new(local)
This is an old version of the currently published article.
  •  

Introduction    

     
Pointers always envied automatic cleanup that is in c++ provided only for big fat slow Static Object or their arrays stored by value. In this article we will introduce concept of scopes also for dynamic objects and scoped version of new/malloc to take advantage of those scopes. Ie associated objects/memory will get destroyed with the scope. Plus your code will now be smaller and simpler. I got this idea about pointers deserving automatic cleanup love too in my other article about new && operator
struct Object : Scope::Obj {
};

Scope global;
// entering scope
void proc() { 
    Scope local;
    char*  text = strdup(local,"text"): // malloc based memory have automatic cleanup too
    
    Object* obj = new(global) Object(); // obj will get destructor called on program exit 

    Object* array[1000]={0};            //sorting pointers is fast compared to array of objects
    for(int i=0;i<1000;i++) {
        array[i] = new(local) Object(); // creates object associated with "local" scope 
    }
}
// we left scope so "Scope local" and all associated object /memory 
// gets destructors called/freed in proper order    

Why manual cleanup is source of bugs.     

Automatic cleanup provided for static objects saves time lines of code and a lot and I mean really lot of bugs. Bugs by forgetting to match all allocations with deallocations. Bugs by memory being leaked when exception or error handler exits something somewhere prematurely and not all control paths contain correct number of release statements.   
Bugs by releasing objects twice or more and corrupting memory by confused condition statements.

Performance point of view    

So we definitely want automatic cleanup that is provided for static objects. But pretty much any program serious about performance will store objects by pointers and not by value because price for reallocation insert or sort is horrendous as you can see in benchmark here

So can't we have both? Performance and efficiency of pointers and safety and simplicity of static objects by automatic cleanup when leaving scope? Yes we can if we implement concept of scopes also for dynamic objects.

Simpler alternative to smart pointers   

Big advantage of scopes is that you don't need kinda heavy dependency on stl/boost just for simple thing like pointer. Code simplicity and clarity keeps bugs at bay. Why ?

in the bigger struct A sample code bellow it's clear what is going on and you are not limited in array storage either. 
 

                 // by keeping simple pointers it's always obvious what is going on 
                 // and how efficiently.
   ptr1 =  ptr2; // just pointers are assigned ie MOVE semantics
  *ptr1 = *ptr2; // objects are copied ie COPY semantics 
                 // ie you decide  when you copy or move by clear syntax. 

With smart pointers the fact when they actually copy or move is one big jungle.

Most of them simply don't work in containers. some require c++11 compiller or big template libs resulting to big executables and all make code unusable by others since libs/dlls  with stl/boost are incompatible on binary level not having any concept of stable interface .  

auto_ptr is very bad choice. It's pointer with copy and with move semantics and ownership (=auto-delete) can't be stored in containers

scoped_ptr is a auto_ptr without copy and without move semanticscan be stored in containers.and requires boost It adds a level of safety. memory created will exist only for that scope and no more, thus you get compile-time protection against attempting to move it or transfer it.  

unique_ptr
 is a auto_ptr without copy but with  move semantics.can be stored in containers requires c++ 11 compillerOwnership is movable,  

shared_ptr. requires c++ 11 compillerIf you want to create something and share that pointer and cleanup when all referencers are gone,  

weak_ptr ,intrusive_ptr ,... whatever new ones they will attempt to create  in future just making the whole mess and confusion even deeper

The real solution is to have smart pointers build in c++ language and not create zillion of weird templates supporting this and not that. For example let Object *~ be declaration of smart pointer. Will have destructor called going out of scope and all of the copy move is already part of low level pointer arithmetics. no zillion of template wrapers/libs/operators. period. I am right now attempting to implement it in CLang compiller as an funny experiment.
 

But back to smart-pointers. Take for example this guy having trouble sorting simple array of pointers and obviously having noidea what state machine he actually created due to geting responses like  

"...The IS specifies that a predicate should not assume that the dereferenced iterator yields a non-const  object. Arguments should therefore be taken by reference-to-const or by value.  The IS also specifies that

algorithms that take function objects as arguments are permitted to copy those function objects. So in most cases, the overloaded function call operator would be a const member function ..."                                                                                                                                            
 
Hmm.. 

It is unbelivable what complexity we vere able introduce to ehm... "simplify our life" 

With smart pointers you need to wrap every pointer reference ie change all the code to alien lenghty syntax always worying about state machine you just created. With scopes you just derive your objects from Scope::Obj in one line and that's all. 

Keeping simple easy to debug code and libs/dlls usable by others. Just insert scope to your class and use scoped de/allocators. All your data will be properly deallocated exactly like with smart pointers even when constructor fails in the middle during exception etc. 

struct A {
    Scope local; 
    B* ptr1;
    B* ptr2;
    A() {
        ptr1=new(local) B();
        ptr2=new(local) B();// will not allocate and will leave prematurely due to memory 
                            // exhaustion exception. But no memory leak will happen since scope
                            // deallocates ptr1 on scope destruction as part of object
                            // destruction that follows this constructor exception
    }
} 

So if you are like me preferring things small and simple so you know by simple look at source what is going on.
Let's try simpler but I hope equally functional alternative.  

Let's Scopes for dynamic heap objects be born  

    So how does the automatic cleanup of static objects actually work? Well. It works by simply adding pointer of every static object within scope to internal invisible linked list and when leaving calling destructors for each of them in reverse order.

Can we make something similar for dynamic objects?
Why not. We can have array of void pointers and store pointers to various object types in one single array.
The only issue are calling proper object specific destructors. Unfortunately as far as I know c++ doesnt allow to get destructor adresses. So the only sollution I come up so far is to use virtual destructor in all stored objects so polymorphysm selects proper one for us.  

Implementation of Scope.h                                                   

This code snippet bellow is kept simple with compressed formating just for purpose of article so it is clear what is going on. Complete implementation along with scoped versions of new delete strdup malloc calloc free and threadsafe version of scope called TScope (usefull for using for example global scope from multiple threads etc) is in Scope.h that is attached in zip file with Example.cpp on top of the article. I did not attached project files since most of you will not be able to open vs2012 anyway. 

struct Scope { // This is Just simple linked list
       struct Obj { virtual ~Obj() {} };
       Scope*    prev;  Obj* ptr;
       Scope() : prev(0) ,   ptr(0) {}	
      ~Scope() { ptr->~Obj(); free(ptr); delete prev; } // deleting all childs in reverse order on exit
};

inline void * __cdecl operator new(unsigned int size,Scope& scope)
{
    void *ptr = (void *)malloc(size); // we add all new objects to this linked list
    if(scope.ptr) { Scope* prev=0; prev=(Scope*)calloc(sizeof(Scope),1); *prev=scope;
    scope.prev=prev; }
    scope.ptr=(Scope::Obj*)ptr; 
    return(ptr);
};

Limitations of this solution 

Only objects derived from Scope::Obj or objects that have virtual destructor as first virtual method are supported.  

Example code  

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "Scope.h"   

Scope global; // Deallocates/Destroys all associated objects on program exit no matter 
              // what exception or error in program happens

struct A : Scope:Obj {// As for supported objects. They must unfortunately be derived
              // from Scope::Obj or have virtual destructor as the first vitual method 
              // so it is first in vtable and correct call is constructed on cleanup
              // In a sense we are keeping pointers to destructors as known this way
       A(){ printf("\n  A %x",this); }
      ~A(){ printf("\n ~A %x",this); } 
};

void Test() {
    Scope local; // all objects associated with scope will be deallocated by leaving procedure
    A* array[3]={0};
    for(int i=0;i<3;i++) {
        array[i]=new(local) A();
    }
    char* text=strdup(global,"this will get deallocated on program exit");
    A* a=new(global) A();  // this will get destructor called on progam exit
}

void main() {
    Test();
}                      

Output: 

   A 689718   A 68b110   A 68b198   A 68b220 ~A 68b198 ~A 68b110 ~A 689718 ~A 68b220

Points of Interest 

The fact that so far I managed to autodeallocate only objects derived from Scope::Obj or with virtual destructor as first virtual method kinda sadens me. But I hope that some way to get around this limit is found since deriving from std::string etc just to allow it's autorelease is kinda unelegant.  

Also if you want to autodeallocate memory from malloc/calloc/strdup just use scoped versions from scope.h in zip file. Off coarse for explicit dealocation you must use  scoped versions of free/delete too so they are removed from scope and no another attempt is made to free them when leaving scope 

 smart-pointer-vs-scop...

 Where this leads and future work

As I said before right now I am playing with the clang c++ compiller sources and implementing smart pointers as build in to c++. Feature that should had been there long time ago (calling destructor when not null and going out of scope) so low level os /driver developers can produce smaller and safer code too without big template monsters like boost. I am open to interesting new ideas or suggestions 

I have no idea how to mark them yet. But so far I am testing with variable*~ syntax
 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ladislav Nevery
Software Developer (Senior)
Slovakia Slovakia
Past Projects:
[Siemens.sk]Mobile network software: HLR-Inovation for telering.at (Corba)
Medical software: CorRea module for CT scanner
[cauldron.sk]Computer Games:XboxLive/net code for Conan, Knights of the temple II, GeneTroopers, CivilWar, Soldier of fortune II
[www.elveon.com]Computer Games:XboxLive/net code for Elveon game based on Unreal Engine 3
ESET Reasearch.
Looking for job

Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
QuestionDidn't know there is a new operator in C PinmemberStan Mihai18-Jul-13 4:41 
QuestionWhy not std::unique_ptr<T>? [modified] PinmemberC++ Pirate Programmer18-Oct-12 5:46 
AnswerRe: Why not std::unique_ptr? [modified] PinmemberLadislav Nevery18-Oct-12 16:02 
QuestionThanks PinmemberYannAchard18-Oct-12 1:29 
AnswerRe: Thanks PinmemberLadislav Nevery18-Oct-12 13:00 
GeneralMy vote of 2 PinmemberSeattleC++15-Oct-12 9:53 
GeneralRe: My vote of 2 PinmemberLadislav Nevery16-Oct-12 9:56 
GeneralRe: My vote of 2 PinmemberSeattleC++16-Oct-12 11:24 
GeneralRe: My vote of 2 [modified] PinmemberLadislav Nevery17-Oct-12 5:44 
GeneralRe: My vote of 2 PinmemberSeattleC++17-Oct-12 10:29 
GeneralRe: My vote of 2 [modified] PinmemberLadislav Nevery17-Oct-12 23:06 
QuestionWhat's about an exception handling? Pinmemberkonik1015-Oct-12 6:05 
AnswerRe: What's about an exception handling? [modified] PinmemberLadislav Nevery16-Oct-12 1:47 
GeneralRe: What's about an exception handling? [modified] Pinmemberkonik1023-Oct-12 21:24 
GeneralRe: What's about an exception handling? PinmemberLadislav Nevery24-Oct-12 22:58 
GeneralRe: What's about an exception handling? Pinmemberkonik1025-Oct-12 1:20 
GeneralRe: What's about an exception handling? PinmemberLadislav Nevery25-Oct-12 4:41 
GeneralRe: What's about an exception handling? [modified] Pinmemberkonik1025-Oct-12 5:43 
GeneralRe: What's about an exception handling? PinmemberLadislav Nevery25-Oct-12 12:50 
QuestionException / Thread safety? PinmemberPetro Protsyk8-Oct-12 1:20 
AnswerRe: Exception / Thread safety? PinmemberLadislav Nevery8-Oct-12 7:54 
GeneralMy vote of 2 PinmemberPatrick Niedzielski7-Oct-12 5:38 
GeneralRe: My vote of 2 [modified] PinmemberLadislav Nevery7-Oct-12 10:26 
GeneralRe: My vote of 2 PinmemberPatrick Niedzielski7-Oct-12 10:30 
GeneralMy vote of 5 Pinmembernv33-Oct-12 22:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140718.1 | Last Updated 3 Oct 2012
Article Copyright 2012 by Ladislav Nevery
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid