Consider the following code which contains a class with overloads for new and new[]:
#include <iostream>
class my_class {
public:
my_class() {};
my_class(const my_class& rhs) {};
~my_class() {};
//the constructor also as applicable.
void * operator new(size_t sz);
void * operator new[](size_t sz);
void operator delete(void * ptr);
void operator delete[](void * ptr);
private:
};
void * my_class::operator new(size_t sz)
{
std::cout << "at operator new, sz is: " << sz << std::endl;
}
void * my_class::operator new[](size_t sz)
{
std::cout << "at operator new[], sz is: " << sz << std::endl;
}
void my_class::operator delete(void * ptr)
{
std::cout << "at operator delete, ptr is: " << ptr << std::endl;
}
void my_class::operator delete[](void * ptr)
{
std::cout << "at operator delete[], ptr is: " << ptr << std::endl;
}
int main(int argc, char ** argv)
{
my_class * ptr = new my_class;
my_class * arr_ptr = new my_class[1];
return 0;
}
Running this in the terminal (Cygwin) I see:
at operator new, sz is: 1
at operator new[], sz is: 9
Why is sz == 9 when operator new[] is called ? Who sets it? What does it depend on (eg. page size?)?
A new-expression, like new my_class does more than call operator new; operator new only allocates memory, and its parameter is the size of the memory it is supposed to allocate.
The parameter sz of operator new is 1 because that's the smallest possible instance; sizeof(my_class) is 1.
The parameter sz of operator new[] is 9 because that is how much memory the new[]-expression (new my_class[1]) requires for allocating an array of 1 instance of size 1.
The "extra" 8 is for keeping track of things like how many elements there are.
The size of the "extra" memory is implementation-dependent.
The runtime "sets" the value when you invoke the operator new. There is an implicit sizeof passed around. It is the same situation as with invoking member functions without implicitly passing around the pointer to the current instance this.
In your case you have a class with no members, so theoretically its size should be zero, however C++ allocates one byte (due to the fact that the objects must at least be addressable). That's why the result of the first allocation is 1 (assuming you fix your operators new/new[] to return something, until then you have UB). The second allocation returns 9 due to how C++ represents dynamic arrays, it puts some header somewhere (implementation defined) so it recognizes it is an array (so when invoking operator delete[], the latter behaves properly) and perhaps due to padding.
Related
I am trying to understand overloading new operator. I wrote the code as below.
#include <iostream>
#include <cstdlib>
#include <new>
using namespace std;
class loc
{
int lo, la;
public:
loc()
{
}
loc(int x, int y)
{
cout << "In constructor\n";
lo = x;
la = y;
}
void show()
{
cout << lo << " ";
cout << la << endl;
}
void *operator new(size_t sz);
void operator delete(void *p);
};
void *loc::operator new(size_t sz)
{
cout << "in Overloaded new\n";
void *p = malloc(sz);
return p;
}
void loc::operator delete(void *p)
{
cout << "in Overloaded delete\n";
free(p);
}
int main()
{
loc *p1 = new loc(10, 20);
p1->show();
delete p1;
return 0;
}
I thought it won't call the constructor because I overloaded the new operator with malloc function call inside overloading function. But the output is as below.
in Overloaded new
In constructor
10 20
in Overloaded delete
That means constructor is getting called. How this is possible? Does this mean will malloc() call constructor?
A new expression results in two separate things happening: allocation of the memory needed by the object begin created, and initialization of the object.
The new operator, on the other hand, just handles the allocation part. When you overload the new operator with respect to a specific class, you are replacing the allocation of memory to the object.
This division of functions makes sense when you realize that not all objects are allocated on the heap. Consider the following case:
int main() {
string someString;
..
}
The local variable is not dynamically allocated; new is not used; however the object still needs to be initialized so the constructor is still called. Note that you did not need to explicitly call the constructor - it is implicit in the language that an appropriate constructor will always be called to initialize an object when it is created.
When you write a 'new expression', the compiler knows to emit instructions to invoke the new operator (to allocate memory as needed) and then to call the constructor (to initialize the object). This happens whether or not you overload the new operator.
The following code I write must invoke both placement deallocation and allocation functions.
#include <iostream>
using namespace std;
struct A
{
void * operator new [] (size_t t, int, int)
{
cout << "3 parameters allocation" << endl;
return ::operator new[](t);
}
void operator delete [] (void *p, int, int)
{
cout << "3 parameters deallocation" << endl;
return ::operator delete[](p);
}
};
int main()
{
A *a = new (5,5) A[10]; //operator new [] (size_t, int, int) invoked
delete [] a; //Error. Overload resolution is failed.
}
demo
5.3.4/22 says N3797:
A declaration of a placement deallocation function matches the
declaration of a placement allocation function if it has the same
number of parameters and, after parameter transformations (8.3.5), all
parameter types except the first are identical. If the lookup finds a
single matching deallocation function, that function will be called;
otherwise, no deallocation function will be called.
It is not implement in C++11 or it is my misunderstanding?
If a class-specific deallocation function is provided, a delete-expression that is not prefixed by the scope resolution operator is ill-formed unless a class-specific deallocation function with one or two parameters is available:
10 - If the type is complete and if deallocation function lookup finds both a usual deallocation function with only a pointer parameter and a usual deallocation function with both a pointer parameter and a size parameter, then the selected deallocation function shall be the one with two parameters. Otherwise, the selected deallocation function shall be the function with one parameter.
So you need to either:
provide void operator delete[](void*);,
provide void operator delete[](void*, size_t);, or
write ::delete[] a.
A *a = new(5, 5) A[10];
A::operator delete[](a,5,5);
On Visual C++ 2013 this works great
If you want to have access to the original placement new parameters in your operator delete, you will have to store them in the allocated memory block and retrieve them later. For example (Live at Ideone):
struct A
{
static void * operator new [] (size_t t, int first, int second);
static void operator delete [] (void *p, size_t t);
static void operator delete [] (void *p, int first, int second)
{
cout << "3 parameters deallocation: " << first << ", " << second << endl;
return ::operator delete[](p);
}
};
// simple POD struct to store the placement parameters
struct info {
int first_param, second_param;
};
void* A::operator new [] (size_t t, int first, int second)
{
cout << "3 parameters allocation: " << first << ", " << second << endl;
// allocate sizeof(info) extra space to store the parameters
auto const p = ::operator new[](t + sizeof(info));
auto const pinfo = reinterpret_cast<char*>(p) + t;
auto const tmp = info{first, second};
std::copy_n(reinterpret_cast<const char*>(&tmp), sizeof(info), pinfo);
return p;
}
static void A::operator delete [] (void *p, std::size_t t) {
// retrieve the parameters that we stored in operator new [].
auto const pinfo = reinterpret_cast<const char*>(p) + t;
auto tmp = info{};
std::copy_n(pinfo, sizeof(info), reinterpret_cast<char*>(&tmp));
cout << "Deleting with original parameters: " << tmp.first_param << ", " << tmp.second_param << endl;
::operator delete[](p);
}
I am using c++11 (g++ v4.7.2)
I've overloaded operator new and operator delete for "Base" class. Apparently they should not call constructor/destructor upon call of new/delete because I've not implemented ctor/dtor call in overloaded new/delete. But the output is contrary to that
//Output of below program
Base new opnd.cpp 87
Base ctor
10
Base dtor
Base delete
Why ctor/dtor are being called for overloaded operator new/delete?
#include <iostream>
using namespace std;
#define NEW new(__FILE__, __LINE__)
#define DELETE delete
class Base
{
public:
Base():m_i(10){ cout << "Base ctor" << endl; }
virtual ~Base(){ cout << "Base dtor" << endl; }
void* operator new(size_t size, const char* file, int line) throw(std::bad_alloc);
void operator delete(void *rawMem, size_t size);
int geti(){ return m_i; }
private:
int m_i;
};
void* Base::operator new(size_t size, const char* file, int line) throw(std::bad_alloc)
{
void *p;
cout << "Base new " << file << " " << line << endl;
//Handle 0 byte requests
if(size == 0)
size = 1;
if(size != sizeof(Base))
{
return ::operator new(size); // To handle new requests for derived classes
}
while(true)
{
p = malloc(size);
if(p)
return p;
new_handler globalHandler = set_new_handler(0);
set_new_handler(globalHandler);
if(globalHandler) (*globalHandler)();
else throw std::bad_alloc();
}
}
void Base::operator delete(void *rawMem, size_t size)
{
cout << "Base delete" << endl;
if(rawMem == 0)
return;
if(size != sizeof(Base))
{
::operator delete(rawMem); //To handle delete requests for derived classes
return;
}
free(rawMem);
}
int main()
{
Base *b = NEW Base;
cout << b->geti() << endl;
DELETE b;
return 0;
}
An operator new function (be it the global one or a class-specific one) is not the entire implementation of a new expression. It's only the allocation function. When you write new T in your code, the following happens:
An appropriate allocation function named operator new is chosen and called to obtain space for the object.
T's constructor is called on the space obtained from point 1.
This means there is no way to bypass constructor invocation by writing your own operator new - invoking the constructor is done by the language, not by the allocation function.
'Operator new' and 'new Operator' are two distinct things. When we use new to create some object like
MyClass* ca= new MyClass();
we are using the 'new Operator'. It does two things:
Calls 'Operator new' to allocate enough memory. It can be overloaded as you did.
Calls the constructor of the object to set initialization data.
These 2 steps will be executed no matter how the space is allocated. Hence after allocating memory using your overloaded 'Operator new', the constructor is called.
I am reading the source code of SGI standard template library. I find that the operator new function always has a double-colon in front of it. Like this:
T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
operator new can be directly called without adding the :: field, then why do the stl coders write it in this way? What trap or situation may it come to if we don't use the :: in front of them.
You can overload operator new for a class and prefixing it with "::" will call the global "default" operator new instead of a possible overload. For example:
#include <iostream>
class Foo
{
public:
Foo() { std::cout << "Foo::Foo()" << std::endl; }
void * operator new(size_t )
{
std::cout << "Foo::operator new()" << std::endl;
return static_cast<Foo *>(malloc(sizeof(Foo)));
}
};
int main()
{
Foo foo;
std::cout << "----------------------" << std::endl;
Foo * p = new Foo;
std::cout << "----------------------" << std::endl;
Foo * q = ::new Foo;
}
will print
Foo::Foo()
----------------------
Foo::operator new()
Foo::Foo()
----------------------
Foo::Foo()
Edit: The code snipped is indeed not about operator new that is defined in a class scope. A better example would be this:
#include <iostream>
namespace quux {
void * operator new(size_t s)
{
std::cout << "quux::operator new" << std::endl;
return malloc(s);
}
void foo()
{
std::cout << "quux::foo()" << std::endl;
int * p = static_cast<int*>(operator new(sizeof(int)));
}
void bar()
{
std::cout << "quux::bar()" << std::endl;
int * p = static_cast<int*>(::operator new(sizeof(int)));
}
} // namespace quux
int main()
{
quux::foo();
quux::bar();
}
which prints
quux::foo()
quux::operator new
quux::bar()
::operator new in C++ is the global memory allocator that is called every time a certain amount of bytes is needed for a single object. Note that the return value is a void * and that ::operator new(size_t) deals with raw bytes, not objects.
It is basically the C++ counterpart of malloc with simply a funny name.
A separate global allocator ::operator new[](size_t sz) is instead used to allocate memory for arrays of objects.
These two operators, their counterparts ::operator delete and ::operator delete[] and also the nothrow version of the allocators are used for all memory needs of the C++ runtime.
They may be implemented in terms of calls to malloc/free or not. What is guaranteed is that malloc and free dont use them (thus you can call malloc and free if you want to reimplement these functions without the risk of infinite recursion).
To call the operator new from the global namespace, in case the user (or someone) else decided to have a new operator.
Double-colon is used to prevent T::operator new() to be called (if
defined).
struct A final
{
int a;
void* operator new(size_t size)
{
//
// Is size always equal to sizeof(A) here?
//
return ::operator new(size);
}
void operator delete(void* ptr)
{
::operator delete(ptr);
}
};
int main()
{
for (auto i = 0; i < 100; i++)
{
delete new A;
}
}
My question is also embedded in the code.
Does the C++ standard guarantee the sizes passed into A::operator new() are always the same?
Update:
Here, just consider A is a final class only.
Quote from the C++11 standard, section 5.3.4 point 10:
A new-expression passes the amount of space requested to the
allocation function as the first argument of type std::size_t. That
argument shall be no less than the size of the object being created;
it may be greater than the size of the object being created only if
the object is an array.
So, yes, it's guaranteed to be the same as the size of the object. Note however that different compilers or different compiler options may alter the actual size of a particular object at compile-time.
No.
Try:
struct B: public A
{
double a;
}
Now the new operator will get a different size when you create a B
int main()
{
for (auto i = 0; i < 100; i++)
{
delete new A;
delete new B;
}
}
PS add a print to see it:
void* operator new(size_t size)
{
std::cout << "S(" << size << ")\n";
//
// Is size always equal to sizeof(A) here?
//
return ::operator new(size);
}
Output:
> ./a.out
S(4)
S(12)
S(4)
S(12)
S(4)