Sign in to follow this  

Need help with bizarre line of code :o)

This topic is 2589 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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)

Share this post


Link to post
Share on other sites
Prefixing something with :: denotes that the one defined in the global namespace should be used. So ::operator new calls the global new operator.

Share this post


Link to post
Share on other sites
:: 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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 clarity

private:
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

Share this post


Link to post
Share on other sites
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").

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Quote:
Original post by adder_noir
Do you really need to go this far for speed?

...

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?
Some people go that far for speed, but you personally only need to go as far as you want for speed. Maybe if your job at The Company is to write a fast string class then you need to go that far for speed but only because that's in your job descrption. If your job description is to release a game in 2 weeks then you could go "eh, 60 FPS is fast enough for a card game" and ship what you have with std::string.

Share this post


Link to post
Share on other sites
Very interesting thanks! I'm always orientating towards 3D FPS up to MMO standard (long term goal) so with that in mind should I always be looking for any/every speed advantage or am I splitting hairs here?

Just two last question befores I can move on,

1)Given that the comp constructor doesn't have another constructor call (the default one in the operator overload) does this mean it is still memory safe because there's no extra allocation going on like there is in the default call within the Operator overload?

2)Should I always call 'delete []' on an array pointer before re-assigning its contents?

Cheers, that's it - really!! :o)

Share this post


Link to post
Share on other sites
Quote:

... should I always be looking for any/every speed advantage or am I splitting hairs here?

No, definitely not. You should instead use standard components (such as std::string) which may already have optimisations (e.g. the "short string optimisation") which suits general usage. For example, while the authors string concatenation avoids an additional allocation, his string class is algorithmically slower for some operations because getting the length of the string is O(N). std::string is O(1) for the same operation.

Writing a large application by your self is hard enough. You must leverage anything to make it easier if you want to finish even a modest game in a reasonable amount of time.

So, don't optimise until you know something needs optimisation. Basically, wait for your program to run slowly on the target hardware. Then, profile to discover the bottleneck (it is rarely where you think it is!). Finally, try to apply high level algorithmic optimisations over low level ones. These generally make the biggest difference for the amount of work involved. Low level optimisations are sometimes associated with bugs because it often involves taking simple but naive code and making it more complicated.

Quote:

1)Given that the comp constructor doesn't have another constructor call (the default one in the operator overload) does this mean it is still memory safe because there's no extra allocation going on like there is in the default call within the Operator overload?

I'm not 100% sure of what you are getting at here - what do you mean by "memory safe"? I will not leak, and it correctly sets up the String invariants, so it is safe.

BTW, the author's terminology of "computational constructor" isn't really standard, there is nothing particularly special about a constructor that does work as well as assignment.

In C++, constructors cannot call other constructors.
Quote:

Should I always call 'delete []' on an array pointer before re-assigning its contents?

No, only if it was allocated with new [], and it is not shared. In general, use std::vector<>, not raw new []/delete[] calls.

Quote:

Oddly tho he declared it as private yet still calls it in this operator overload.

The operator overload is a "friend", otherwise it could not access the private constructor or private "s_" members.

Share this post


Link to post
Share on other sites
That's great thanks, I understood everything you said pretty much cheers pal thanks to you I can now move on to the next chapter.

Doubt it will be long before I'm posting again though :oD

Share this post


Link to post
Share on other sites

This topic is 2589 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this