Thread: Operator new

  1. #1
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99

    Operator new

    Hello. Two questions on operator new:
    1. When do you need to include the <new> header? I am able to use and overload the normal new and placement new operators even if I don't include it.

    2. The following trivial program doesn't compile.

    Code:
    #include <iostream>
    using namespace std;
    
    struct X
    {
        int i;
        int j;
        X(int a = 0, int b = 0): i(a), j(b){}
        ~X(){}
        void* operator new(size_t, void* p)
        {
            cout << "an overloaded class X new operator" << endl;
            return p;
        }
    };
    
        void* operator new(size_t, void* p, char* s)
        {
            cout << s << ": ";
            cout << "an overloaded global-scope new operator" << endl;
            return p;
        }
    
    int main()
    {
        char* buffer = new char[sizeof(X)];
        X* p = new(buffer, "test") X(99, 99);
        p->~X();
        delete[] buffer;
        return 0;
    }
    However, if I remove the overloaded operator new in class X it works fine. Alternatively, I can leave the operator new in class X as it is and just add a :: to the 4th last line, i.e.
    Code:
    X* p = ::new(buffer, "test") X(99, 99);
    Then it works fine two. The operator new in class X is obviously hiding the global operator new - even though it takes the wrong arguments for the call in question. Is that normal?

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Well, you are passing 3 arguments to new (the first one being "behind the scenes"), so the overloaded operator must also take 3 arguments (it's missing the char* argument).
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    That's my point. Since the class overloaded operator has got the wrong no. of arguments and the global one has the right no., why does the former hide the latter?

  4. #4
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Since the class overloaded operator has got the wrong no. of arguments and the global one has the right no., why does the former hide the latter?
    I couldn't explain it if I tried. To put it simply, it "hides" the global precisely because it doesn't have the same number of arguments. You've not "overloaded"; you've "overridden". This is the almost the same problem with attempting to overload in a derived class.

    Soma

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I think part of the problem here is the fact that C++ automatically uses the operator new from the class of which you are creating an object from [in this case struct X] when there is an overloaded operator new in the class. If there is no matching form that matches the class and arguments to new, then it will give a compiler error, not pick a different variant of new from some other class (or the global variant) simply because those have the right argument set. It also works this way for operator new in base-class of a derived class - it calls operator new for the base-class [unless the derived class implements it's own operator new of course].

    The reason it does this, I'm fairly sure, is so that you do not have to KNOW if a class overloads new or not - it would be very hard to write generic code if it didn't work this way [think template code such as STL that may need to create a "new" copy of a class, which is supplied to the code itself from code that the STL programmer doesn't know about].

    It would of course also make a whole lot more work for the programmer(s) if we had to change all calls to new in an existing piece of code, just because we have added a operator new() to an existing class that is widely used.

    Where I work, we have certain base-classes that provide an operator new - the main purpose here is to clear the content of the class to zero, so operator new essentially does:
    Code:
    class Base
    {
      ...
    public:
       void *operator new(size_t size)
       {
           void *ptr = malloc(size);
           if (!ptr)  
                 error();
           memset(ptr, 0, size);
        }
      ...
    }
    This would not work as nicely if you had to know that the Base class has an operator new implemented.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    When do you need to include the <new> header?
    When you want to use nothrow new, or you want to catch bad_alloc exceptions specifically.

    The operator new in class X is obviously hiding the global operator new - even though it takes the wrong arguments for the call in question. Is that normal?
    Yes. The C++ name lookup rules say that the compiler first looks for the name, and when it has found it, it chooses the correct overload from all the functions by that name in the found scope. It's the same with member functions from a base:
    Code:
    struct Base { void f(); };
    struct Derived : Base { void f(int); };
    int main() {
      Derived d; d.f(); // error: no suitable overload found, candidate is Derived::f(int)
    }
    You've not "overloaded"; you've "overridden".
    No, overriding only means overriding a virtual function. What you mean here is simply "hiding".
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #7
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    What you mean here is simply "hiding".
    No. I mean exactly what I said. I attempted to provide additional context. Did you miss the part where I said "hides"?

    How you may wish to interpret what I say is none of my concern.

    We had this argument over the names of iterators. Not all definitions are your definitions. The C++ standard is not the last word in technical vocabulary. (I have a tendency to borrow vocabulary and slang from everyone. If you want to limit all discussion in such a fashion it would probably be best to ignore me.) This time I'm not going to argue with you.

    Soma

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    That's just stupid. Hiding is a perfectly good word to describe the situation. Why would you want to use a longer word that has a completely different meaning for that?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #9
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    That does seem to be a bit at odds with the usual lookup rules though, where only those functions are considered whose formal parameters match the arguments in the function call.

  10. #10
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by DL1 View Post
    That does seem to be a bit at odds with the usual lookup rules though, where only those functions are considered whose formal parameters match the arguments in the function call.
    Yes, but the whole point of making a class-specific operator new is that you want the class to be created through that interface. So by looking in the global namespace for a new operator when the class has it's own operator would violate that. Just like this:
    Code:
    class X
    {
       public:
          static int func(int a);
    };
    
    int func(int a, int b);
    
    int main()
    {
       class X x;
       x.func(1, 2);
       return 0;
    }
    You don't expect this code to use the ::func(), do you?

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by DL1 View Post
    That does seem to be a bit at odds with the usual lookup rules though, where only those functions are considered whose formal parameters match the arguments in the function call.
    The normal lookup rules work exactly like those for operator new.
    Code:
    void f() {}
    
    namespace ns {
    	void f(int);
    
    	void g()
    	{
    		f();
    	}
    }
    
    int main()
    {
    	ns::g();
    }
    results in:

    error C2660: 'ns::f' : function does not take 0 arguments

    Lookup by name comes first, resolution of overloads second. No exceptions.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    matsp

    No, of course not. x.func(1, 2) by definition calls a member function of X (surely?). In my example, however, the global function is, in itself, a valid alternative - if the X member function is deleted, the compiler is happy to use the global function.

    cornedbee

    I think that is a bit different. g is looking for a f from inside ns, so void f(int) is bound to hide void f(). Although I suppose it's not really different if you proceed from what matsp has been saying...

    Anyhow, thanks for the help. I get the point now.

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Ok, so to use Scott Meyers principle of explaining opertor behaviour (and explain better what both cornedbee and me have been saying):
    Code:
    X *x = new X;
    translates to:
    Code:
    X *x = X::operator new(sizeof(X));
    The compiler will issue it's own version of operator new (just like it makes constructors and destructors if you don't specify any) for default "just size" variant.

    So, no the global option doesn't actually work here.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    That's not correct. The lookup rules for operator new are special in that they look in the class first and then in the global namespace. Nothing else does that outside the context of the class. Operator new is also special in that it is the only entity that may appear in global scope, but not in namespace scope. (Well, it may appear, but it won't be looked up for the new operator.)

    If the transformation as you describe it were correct, you could never use a global placement new for any class type.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  15. #15
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by CornedBee View Post
    That's not correct. The lookup rules for operator new are special in that they look in the class first and then in the global namespace. Nothing else does that outside the context of the class. Operator new is also special in that it is the only entity that may appear in global scope, but not in namespace scope. (Well, it may appear, but it won't be looked up for the new operator.)

    If the transformation as you describe it were correct, you could never use a global placement new for any class type.

    Ok, so the words I use to describe the process may not be what you use, but essentially, you could say that "if you do not provide one, the compiler does it for you" in the sense that it uses the global one.

    I do agree with your description of how it works, just in case the above comes across as different - that's how the compiler goes about it.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed