Need help with bizarre line of code :o)

Started by
12 comments, last by adder_noir 13 years, 5 months ago
Hi!

Plodding on as always and I've hit my latest problem in my book. It is a line of code which I have never, ever seen anything like it before. I would try and add some of my own observations, but the truth is, I don't have any. It's just too strange for me to understand:

String* names = static_cast<String*>(::operator new(BUFSIZ));


It feels like so long since I was asking about the new cast operators I can barely recall what static_cast does when opposed to dynamic_cast and the like but hey that's my issue I can always do some research again if I have to.

'String' is a user defined class type and obviously:

(::operator new(BUFSIZ));


returns a pointer which gets cast to a String* and BUFSIZ is probably a constant int. I don't get at all why the '::' is present or the 'operator new' is present either.

I would appreciate any help on this one, I'm really confused thanks :o)
Advertisement
Prefixing something with :: denotes that the one defined in the global namespace should be used. So ::operator new calls the global new operator.
:: is the scope resolution operator. It's usually found following a namespace, such as std:: With nothing before it, it refers to the default namespace.

operator new is an operator. You're just selecting a specific implementation of new, rather than letting the compiler select one for you.

That code doesn't do anything that just calling new String() wouldn't, unless String has some custom operator new that you're trying to bypass. And since you're bypassing it, maybe it shouldn't have it.
Quote:Original post by adder_noir
Hi!

Plodding on as always and I've hit my latest problem in my book. It is a line of code which I have never, ever seen anything like it before. I would try and add some of my own observations, but the truth is, I don't have any. It's just too strange for me to understand:

String* names = static_cast<String*>(::operator new(BUFSIZ));


It feels like so long since I was asking about the new cast operators I can barely recall what static_cast does when opposed to dynamic_cast and the like but hey that's my issue I can always do some research again if I have to.

'String' is a user defined class type and obviously:

(::operator new(BUFSIZ));


returns a pointer which gets cast to a String* and BUFSIZ is probably a constant int. I don't get at all why the '::' is present or the 'operator new' is present either.

I would appreciate any help on this one, I'm really confused thanks :o)



Lets break it down:

String* names


declares a new pointer to a String object

static_cast<String*>(B)


casts from a pointer type B to a String*

::


Says "Use the global scope version of the function".

operator new(BUFSIZE)


Call the function corresponding to operator new, allocating BUFSIZE bytes. since :: is used, it's specifying to NOT use a locally defined operator new, and use the one at global scope.


I'm guessing this line of code is in some kind of overloaded new operator for the String* class. Otherwise, there are better ways to write this code.

That's great thanks! I'll have a good read through and reply more thoroughly tomorrow. Thanks much to you three guys who posted, especially the last guy who went into alot of detail :oD
OK great, here's where I'm up to. I read a bit further on and it seems the author is using this example to show how not to perform assignment. I'll elaborate. The class 'String' has the following operator overload defined within it:

String& String::operator = (const char* str){    if(!str) str = "";    char* tmp = strcpy(new char[strlen(str)+1], str);    delete [] s_;    s_ = tmp;    return *this;}//added this bit for clarityprivate:    char* s_; // s_ is a private data member


So from what you guys are saying the '::' guarantees that the default namespace will be used, so no chance of a user defined implementation of 'new' can occur. Seems that according to the author:

(::operator new(BUFSIZE));


will just produce a pointer to an uninitiated piece of memory with BUFSIZ bytes. This is new to me as I've never seen the keyword 'new' used outside of defining a new array dynamically for example:
int* myPointer = new int[5];
I guess this code line would produce an 'initialised' pointer right?

ANyway moving on, this is the line where the author states there'll be trouble:
String *names = static_cast<String*>(::operator new(BUFSIZ));names[0] = "Sakamoto"; // THIS LINE HERE


Ok.Now I'm not sure why he's used 'names[0]' when he could have just used 'names'. I always thought a pointer's identifier pointed to the first element in an array anyway. I don't even know where he's used the '[]' operator here for that matter I thought 'names' was just a pointer to one class instance of class String. Hmmm. Could use some help with that one to be honest if possible.

Ok so thus far I'm kind of with him. If you use the second line of code shown above you run the String class's assignment operator overload and end up running an array delete (delete []) on an uninitialised pointer that is just pointing to whatever - so I guess this runs the risk of undefined behaviour and is therefore a no no.

Ok but.... what I don't get is how you can run the String class's assignment operator overload when you don't actually have an instance of that class an existence yet. So far as I can see all there is, is a pointer to type String which is being made equal to a rough pointer cast to a String pointer. I don't see how anywhere this makes the String class's inner workings available, so:

Can you access a class's member functions through a pointer to a class even though there is no actual class object in existence?

Sorry this dragged on, but I did quite a bit of thinking about it and you guys did take the time to reply, so I though I'd honour that by showing some enthusiasm :oD
The author is showing what happens when you call a function on uninitialised memory.

Firstly, operator new is the part of a new expression that allocates memory. Then, placement new is used to call the constructor on this memory to create the object. The author is avoiding the constructor call by using raw operator new.

Here is an example of how scalar new generally works:
String *string = new String();// approx ==void *memory = operator new(sizeof(String));try {   String *string = new(memory) String(); // "placement new" call} catch(...) {   operator delete(memory);   throw;}

Quote:
This is new to me as I've never seen the keyword 'new' used outside of defining a new array dynamically for example:

You've seen scalar new, right? As in Object *object = new Object();"

Quote:
Ok.Now I'm not sure why he's used 'names[0]' when he could have just used 'names'. I always thought a pointer's identifier pointed to the first element in an array anyway. I don't even know where he's used the '[]' operator here for that matter I thought 'names' was just a pointer to one class instance of class String. Hmmm. Could use some help with that one to be honest if possible.

Remember that (operator overloading aside) array access is similar to pointer arithmetic. That is, ptr[index] == *(ptr + index). This means that names[0] is the same as *(names + 0) which is the same as *names.

I don't know why the author chose to use the array method, but it is legal.

Quote:
Can you access a class's member functions through a pointer to a class even though there is no actual class object in existence?

Yes and no. The compiler only cares about the types involved. With some static_cast<> hackery, the author managed to obtain a pointer to type String that doesn't actually point at a string. The behaviour is undefined (hence the "no").
I'll keep it simple.

::operator new() is the C++ equivalent of the C function malloc(). Plain and simple. It differs in that it may throw std::bad_alloc instead of returning NULL for an out-of-memory condition.

If you allocate memory for an object using either ::operator new() or malloc() all you have is allocated, uninitialized memory. You have not constructed an object. No constructors are invoked. You do not have a valid object (what I tell you three times is true). The C++ syntax is ugly because, like reinterpret_cast<>(), you probably don't want to do this and it should be the first place you should look to find bugs.

Stephen M. Webb
Professional Free Software Developer

That's great thanks to you both ;o) Clears it all up alright cheers for some seriously extensive replies ;o)

If I may I'd like to progress with gratitude to the next and I hope to God last question on this part so I can move on to the next Chapter because I've been on this one well over a week! :oO

He mentions and writes a thing called a computational constructor. Apparently constructors operate faster than assignment operations is this definitely correct? Would I really need to worry about such a thing outside of anything that had 1000's of identical(ish) classes being constructed in one executable i.e. game? Do you really need to go this far for speed?

Out of interest he does the following:

private:String(const char*, const char*);//computational constructor! ;o)......//which is then defined outside the class in the following fashion:String::String(const char* a, const char* b){    s_ = new char[strlen(a) + strlen(b) + 1];    strcat(strcpy(s_,a), b); // not sure what this does but it's not important - I can figure it out}


HE actually calls this computational constructor within a + operator overload. In fact the 'return' statement invokes this comp constructor. Have a look:

const String operator + (const String& a, const String& b){return String(a.s_, b.s_);}


I see what he's doing. He takes the two instance of String a and b and uses the compu constructor in the return statement. Crafty I suppose if it helps with speed. Returns I assume a third and new instance of String which is a compound of the two arguments. Oddly tho he declared it as private yet still calls it in this operator overload.

Anyway I'd appreciate any comments anyone has, maybe point out where I'm wrong or have made bad assumptions or haven't really grasped this well. I kinda feel I've got it but not srue.

Also can anyone answer if this would be needed in a game scenario>? Maybe something like vegetation which had many copies of one class could use something like this for speed? Can't think how that would actually happen tho just the two thoughts ran close to each other in my brain. Any ideas?

Thanks :o)
Quote:Original post by adder_noir
He mentions and writes a thing called a computational constructor. Apparently constructors operate faster than assignment operations is this definitely correct?
They are both just functions so it isn't inherently correct. If a computational constructor makes things operatore faster it is because it is used to perform fewer operations than any hypothetical alternative. How would you implement operator+ (an alternative, if you will) without the computational constructor? Probably something like this:
const String operator + (const String& a, const String& b)  String temp;  delete[] temp.s_; // necessary to prevent leaks if String::String() allocates memory  temp.s_ = new char[strlen(a.s_) + strlen(b.s_) + 1];  strcat(strcpy(temp.s_,a.s_), b.s_);  return temp;}
Ok, what does this do compared to the computational constructor? It calls the default constructor when you create temp. It deletes allocated memory from temp to prevent leaks. It allocates data for the new string (also done in the computational constructor). It returns temp.

So, if String::String() does, really, anything at all, then the above overload will be inherently slower than using the computational constructor.

C++: A Dialog | C++0x Features: Part1 (lambdas, auto, static_assert) , Part 2 (rvalue references) , Part 3 (decltype) | Write Games | Fix Your Timestep!

This topic is closed to new replies.

Advertisement