user2953119
user2953119

Reputation:

Segmentation fault instead of constructor call

I'm trying to a bit modified of built-in allocation function:

#include <iostream>
#include <cstdlib>
#include <new>

struct A
{
    int a;
    A(){ std::cout << "Constructor\n"; a = 3; }
    void* operator new(std::size_t t) noexcept
    {
        ::operator new(t);
        return NULL;
    }
};

int main()
{
    new A();
}

demo

Instead of constructor call I've got segmentation fault. Could you explain that behavior?

Upvotes: 8

Views: 964

Answers (3)

Ben Voigt
Ben Voigt

Reputation: 283921

You have a bug, because the call to ::operator new() could throw std::bad_alloc, violating the exception-specification of your class-specific allocator (zybox's answer shows how to fix this). However, that's very unlikely to happen in such a small program. It's also a bad idea to override operator new() without also providing operator delete(), although I can't find an explicit requirement that both must be found in the same scope.

Returning a null pointer from your allocator is both legal and the correct way to indicate an allocation failure. In 3.7.4.1, the Standard says that:

An allocation function that fails to allocate storage can invoke the currently installed new-handler function (18.6.2.3), if any. [ Note: A program-supplied allocation function can obtain the address of the currently installed new_handler using the std::get_new_handler function (18.6.2.4). — end note ] If an allocation function declared with a non-throwing exception-specification (15.4) fails to allocate storage, it shall return a null pointer. Any other allocation function that fails to allocate storage shall indicate failure only by throwing an exception (15.1) of a type that would match a handler (15.3) of type std::bad_alloc (18.6.2.1).

Then in 5.3.4:

If the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.

The code is legal on the path that ::operator new() doesn't throw -- the expression new A() in main() evaluates to a null pointer, which is ok because it never gets dereferenced.

You shouldn't get a constructor call either. What you should get is a memory leak, since there is no ::operator delete() call corresponding to the ::operator new(t) inside your allocator.

Upvotes: 5

TonyB
TonyB

Reputation: 188

The original code simply returns NULL from A::operator new, quietly forgetting the return result of ::operator new (a memory leak). The reason for the crash is that the pointer returned is then used as the this pointer for the new A object. Since that pointer is NULL, anything the constructor does with the object (assigning a = 3) is dereferencing NULL. That results in a segmentation violation. zyboxinternational's answer above gives a good solution, and the comments of both previous posters are very relevant.

Upvotes: 0

AStopher
AStopher

Reputation: 4561

Your function operator new contains a null pointer, and as such doesn't actually return anything.

The null function operator is equal to return false;, or just return;, but be warned: duplicating a pointer using the new operator causes memory leaks if you aren't careful. To get around this, just delete(t) when you're done.

The following code will fix your issue:

#include <iostream>
#include <cstdlib>
#include <new>

struct A
{
    int a;
    A(){ std::cout << "Constructor\n"; a = 3; }
    void* operator new(std::size_t t) noexcept
    {
        return ::operator new(t, std::nothrow);
    }
};

int main()
{
    new A();
}

Upvotes: 1

Related Questions