It's known that defining a heap variable with new gets the pointer without specifying the name:
Var *p = new Var("name", 1);
But I have to clear the variable pointed to by p with delete p later on in the program.
I want to declare a stack variable so it is automatically cleared after function exits, but I only want to get the pointer, and the following:
Var v("name", 1);
Var *p = &v;
is quite tedious, and specifier v will never be referenced.
Can I declare a stack class instance and get its pointer without specifying its name?
There's two questions hidden in here. The first one is:
Var *p = new Var("name", 1);
But I have to clear the variable pointed to by p with delete p later
on in the program.
I want to declare a stack variable so it is automatically cleared
after function exits
So here, you're asking how to allocate memory without having to explicitly clean it up afterwards. The solution is to use std::unique_ptr:
std::unique_ptr<Var> p(new Var("name", 1));
Voila! unique_ptr will automatically clean itself up, it has virtually no overhead compared to a raw pointer, and it's overloaded the * and -> operators so you can use it just like a raw pointer. Search for "C++11 smart pointers" if you want to know more.
The second question is:
I only want to get the pointer, and the following:
Var v("name", 1);
Var *p = &v;
is quite tedious, and specifier v will never be referenced.
The important point here is that Var *p = &v is completely unnecessary. If you have a function that requires a pointer, you can use &v on the spot:
void SomeFunc(const Var* p);
// ...
Var v("name", 1);
SomeFunc(&v);
There's no need to put &v in a separate variable before passing it into a function that requires a pointer.
The exception is if the function takes a reference to a pointer (or a pointer to a pointer):
void SomeFunc2(Var*& p);
void SomeFunc3(Var** p);
These types of functions are rare in modern C++, and when you see them, you should read the documentation for that function very carefully. More often than not, those functions will allocate memory, and you'll have to free it explicitly with some other function.
There's no way to do this by allocating on the stack. However, you can use std::make_shared for the heap:
#include <memory>
std::shared_ptr<Var> p = std::make_shared<Var>();
At the cost/risk of being more confusing, you can avoid repeating the type in your code in the question ala:
Var v("name", 1), *p = &v;
You could also potentially use alloca, which is provided by most systems and returns a pointer to stack-allocated memory, but then you have to go through a separate painful step to placement new an object into that memory and do your own object destruction. alloca needs to be called inside the function so it's the function stack on which the object is created, and not during the preparation of function arguments (as the variable's memory may be embedded in the stack area the compiler's using to prepare function arguments), which makes it tricky to wrap into some easily reused facility. You could use macros, but they're evil (see Marshall Cline's C++ FAQ for an explanation of that). Overall - not worth the pain....
Anyway, I recommend sticking with the code in your question and not over-thinking this: using &v a few times tends to be easier, and when it's not it's normally not a big deal if there's an unnecessary identifier for the stack-based variable.
I don't think there is a way to overcome it without some overhead (like the shared_ptr). so the shortest way to write it will be:
Var v("name", 1), *p = &v;
Yes, it's possible to return an address to a temporary (i.e. stack) object and assign it to a pointer. However, the compiler might actually discard the object (i.e. cause that section in memory to be overwritten) before the end of the current scope. (TO CLARIFY: THIS MEANS DON'T DO THIS. EVER.) See the discussion in the comments below about the behavior observed in different versions of GCC on different operating systems. (I don't know whether or not the fact that version 4.5.3 only gives a warning instead of an error indicates that this will always be "safe" in the sense that the pointer will be valid everywhere within the current scope if you compile with that particular version of GCC, but I wouldn't count on it.)
Here's the code I used (modified as per Jonathan Leffler's suggestion):
#include <stdio.h>
class Class {
public:
int a;
int b;
Class(int va, int vb){a = va; b = vb;}
};
int main(){
Class *p = &Class(1, 2);
Class *q = &Class(3, 4);
printf("%p: %d,%d\n", (void *)p, p->a, p->b);
printf("%p: %d,%d\n", (void *)q, q->a, q->b);
}
When compiled using GCC 4.5.3 and run (on Windows 7 SP1), this code printed:
0x28ac28: 1,2
0x28ac30: 3,4
When compiled using GCC 4.7.1 and run (on Mac OS X 10.8.3), it printed:
0x7fff51cd04c0: 0,0
0x7fff51cd04d0: 1372390648,32767
In any case, I'm not sure why you wouldn't just declare the variable normally and use &v everywhere you need something "pointer-like" (for instance, in functions that require a pointer as an argument).
Related
CppCon 2015: Herb Sutter "Writing Good C++14... By Default" Slide 50
Live on Coliru
I have seen the following guideline through above talk. However, I have hard time to understand the critical issues both solutions try to solve in the first place. All comments on the right side of the code are copied from original talk. Yes, I don't understand the comments on the slide either.
void f(int*);
void g(shared_ptr<int>&, int*);
shared_ptr<int> gsp = make_shared<int>();
int main()
{
// Issue 1>
f(gsp.get()); // ERROR, arg points to gsp', and gsp is modifiable by f
// Solution 1>
auto sp = gsp;
f(sp.get()); // ok. arg points to sp', and sp is not modifiable by f
// Issue 2>
g(sp, sp.get()); // ERROR, arg2 points to sp', and sp is modifiable by f
// Solution 2>
g(gsp, sp.get()); // ok, arg2 points to sp', and sp is not modifiable by f
}
Can someone please give me some advices what the problems are if we write code shown in Issue 1 and Issue 2 and why the solutions fix the problems?
f could be written like this:
void f(int* p)
{
gsp = nullptr;
*p = 5;
}
Since gsp is the only shared_ptr that owns the int, if it is cleared, then the pointer given to p is destroyed.
Basically, it is reasonable for functions which are given non-owning pointers (or references) to expect that the objects being pointed to are owned by someone outside of themselves, and that there is nothing they can do to cause them to be destroyed.
g is something similar, though with a reference as a parameter rather than a modifiable global:
void g(shared_ptr<int> &sp, int* p)
{
sp = nullptr;
*p = 5;
}
Both of these are warnings about making sure that, if some code passes an owned object to a function without explicitly transferring ownership, that function must ensure that there is nothing the called function can do to destroy that owned object by accident. In real-world scenarios, these will be fair more complex, as far as determining what has actually happened.
For cases of g, the parameter might be a reference to some complex object which happens to have the last shared_ptr to the int you also passed as a parameter. For f, the global object might be some "singleton" that manages a bunch of things, including ownership of objects, and this function just so happened to be passed a pointer to such an object. Inadvertently calling the wrong function from f or g can cause the pointer to become invalid.
Raw pointers have caused a lot of problems in C++. The key problem is that their lifetime is totally unclear. If I have a T*, then I have no idea how long that T is supposed to live, or if I'm supposed to free it, or if the caller will free it. Messing that up can result in double frees, memory leaks, and in the worst case undefined behavior.
So Herb Sutter, in his presentation, places some basic rules on the use of raw pointers. In particular, he assumes implicitly that raw pointers are always borrowed. That is, a T* never owns the underlying T and hence should never free it. (If you, for some reason, need an owned T* and can't use a smart pointer, the C++ Core Guidelines recommend using an owned<T*> type to make your intentions clear, even if the owned type itself doesn't change anything semantically)
So a T* is borrowing data. If we borrow data, we need to know that it will live at least as long as the place we're borrowing it. That's where the top of Slide 50 comes in.
In callee, assume Pointer params are valid for the call, and independent.
So, under Sutter's convention, a function that takes a T* should assume that the T* is good for at least until the function returns.
Now, if I pass the innards of a shared_ptr, it's possible that I'll invalidate it accidentally, without even knowing about it.
shared_ptr<int> gsp = make_shared<int>();
void f(int* my_pointer) {
gsp = some_other_data;
// Oh no! my_pointer is suddenly garbage!
}
int main() {
f(gsp.get());
}
At the "Oh no!" point in the code above, dereferencing my_pointer is undefined behavior. But we couldn't have possibly known that, since we never actually did anything with that pointer, just with a (theoretically unrelated) pointer.
The data a shared_ptr points to is only freed when all of the shared pointers referencing it are gone. So by creating a local variable in main that also points to the same data, we ensure that the data won't be freed until main ends, even if the function accidentally invalidates the one reference that it does have access to.
The g example is showing that the same thing can happen even without global variables. If a function takes a std::shared_ptr<T> and a T* that reference the same data, then the former could invalidate the latter unless there's another shared_ptr<T> somewhere else that the callee doesn't have access to that's anchoring it into position.
I came across an issue today regarding local variables. I learned that...
int * somefunc()
{
int x = 5;
return &x;
}
int * y = somefunc();
//do something
is bad, unsafe, etc. I'd imagine that the case is the same for...
int * somefunc()
{
int * x = new int;
x = 5;
return x;
}
int * y = somefunc();
//do something
delete y;
I've been under the impression for the longest time that this would be safe as the address of x stays in scope when it's returned. However, I'm having second thoughts now and I'm thinking this would lead to memory leaks and other problems, just as the fist example would. Can someone confirm this for me?
As it stands, the second example is wrong. You probably meant this:
int * somefunc()
{
int * x = new int;
*x = 5; // note the dereferencing of x here
return x;
}
Now this is technically fine, but it is prone to errors. First, if after the allocation of x an exception happens, you have to catch it, delete x and then rethrow, or you get a memory-leak. Second, if you return a pointer, the caller has to delete it - callers forget.
The recommended way would be to return a smart pointer, like boost::shared_ptr. This would solve the problems mentioned above. To understand why, read about RAII.
Yes, you're taking the risk of leaking memory. (compile errors aside.)
Doing this for an int is silly, but the principle is the same even if it's a large structure.
But understand: you've written C-style code, where you have a function that allocates storage.
If you're trying to learn C++, you should put somefunc() and the data it operates on into a class. Methods and data together. A class can also do RAII as Space_C0wb0y pointed out.
You might be making int * as just an example, but really, in the case you noted, there is not a reason to return int *, just return int, the actual value is more than good enough. I see these situations all the time, getting overly complicated, when, what is actually needed, is just to simplify.
In the case of 'int *', I can only really think of a realistic case of returning an array of ints, if so, then you need to allocate that, return that, and hopefully, in your documentation, note that it has to be released.
The first approach certainly leads to problems, as you are now well aware.
The second is kind of OK, but demands attention from the programmer because he needs to explicitly delete the returned pointer (as you did). This is harder when your application grows larger, using this method will probably cause problems (memory leaks) as the programmer will find it difficult to keep track of every single variable he needs to deallocate.
A 3rd approach for this scenario, is to pass a variable by reference to be used inside the function, which is way safer.
void somefunc(int& value)
{
value = 5;
}
// some code that calls somefunc()
int a_value = 0;
somefunc(a_value);
// printing a_value will display 5
(Edited)
Yes, the second is fine, so long as you dereference that 'x' before assigning!
Ok, I would analyze this by answering these questions:
What does x contain ? - A memory location(since it is a pointer
variable)
What is the scope of x? - Since it a a auto variable it's scope is
limited to the function somefunc()
What happens to auto variables once they exit the local scope ? - They are
deleted from the stack space.
So what happens to x now after return from somefunc()? - Since it is
an auto variable declared on the stack
, it's scope(lifetime) is limited to
somefunc() and hence will be deleted.
Ok so now, what happens to the value pointed to by x? We have a
memory leak as the value is allocated
on the heap and we have just lost the
address when x is deleted.
What does y get? - No idea.
What happens when y is deleted? - No idea.
The point is not to return a pointer or reference to a local variable, because once the function returns, locals don't exist.
However, the return value still exists, and dynamically allocated memory certainly exists as well.
In C++, we prefer to avoid raw pointers whenever possible. To "return a value that already exists" (i.e. the function does not create a new value), use a reference. To "return a value that didn't already exist" (i.e. the function creates a new value, in the idiomatic sense, not the new keyword sense) use a value, or if necessary, some kind of smart pointer wrapper.
It's both memory leak and a crash (because of the delete).
struct run_male_walker_struct {
string male_user_name;
string show_name;
};
typedef struct run_male_walker_struct run_male_walker_struct_t;
in another function:
run_male_walker_struct_t *p = malloc(sizeof(struct run_male_walker_struct));
question, is it illegal? As the string is a class, it's size can't be determined by sizeof().
This is illegal, but not for the reasons you're thinking.
The difference between std::malloc()/std::free() and new/delete is that the latter will call constructors/destructors, while the former won't. The expression
void* p = std::malloc(sizeof(run_male_walker_struct))
will return a blob of uninitialized memory on which no constructor is called. You shouldn't touch it with a ten foot pole - except for invoking a constructor on it:
run_male_walker_struct* pw = new(p) run_male_walker_struct;
If you do this, you will have to do the reverse, too:
pw->~run_male_walker_struct();
before you free the memory:
std::free(p);
However, that leaves the question why you want to do that.
The only reason to do this should be when you want to separate memory allocation from construction (like, for example, in a pool allocator). But if you need that, it's best hidden behind some interface. A natural one would be overloading new and delete per class. Also, std::vector does this internally.
Not really sure what you're asking here... Just to be clear, the struct keyword is a valid C++ designation, that functions nearly identically to class except for the default privacy. So if you're compiling with g++, and including the string library, this is a valid statement.
However, calling with malloc() will just give you the memory, not actually construct the values inside that struct. You could more appropriately instantiate it by calling it's default constructor.
The struct definition itself is fine. It results is a non-POD aggregate. But you should prefer the use of new and delete over malloc and free because these handle construction and destruction properly. If you want to keep using malloc and free you have to use the placement-new to properly construct the object and invoke the destructor manually to destroy it before you free it:
#include <new>
...
run_male_walker_struct *p = (run_male_walker_struct*)
malloc(sizeof(run_male_walker_struct));
new(p) run_male_walker_struct; // <-- placement-new
...
p->~run_male_walker_struct(); // <-- pseudo destructor call
free(p);
Or simply:
run_male_walker_struct *p = new run_male_walker_struct;
...
delete p;
BTW: the typedef is not necessary in C++
Try not to use malloc, if you are in C++.
Using NEW is a better alternative, when you browse into the NEW() code, you will realize it does call malloc!!!
The pros of using NEW is it will call the constructor of your class instantiated.
Another minor comment, the code you provided should not be compilable:
run_male_walker_struct_t *p = malloc(sizeof(struct run_male_walker_struct));
Should be
run_male_walker_struct_t *p = (run_male_walker_struct_t*)malloc(sizeof(struct run_male_walker_struct));
this is due to malloc will return a void*.
Using malloc() would work, but using it will only create enough space for your struct.
This means that you will not be able to use your strings properly, because they weren't initialised with their constructors.
Note that string classes don't have their contents in stack memory, but in dynamic memory, which doesn't affect the size of the struct. All classes and structs have a static size, that are known at compile-time (if the struct/class was defined).
I would suggest using new. Using malloc will stuff up the strings.
This raises a question of my own, how did constructors get called on dynamically allocated instantiation in C (were there no such things as constructors in C?). If so, yet another reason against using pure C.
How about
run_male_walker_struct_t * p = new run_male_walker_struct_t:
I'm fairly sure this is legal because the size of the std::string object will be known even if the lengths of the strings are not known. The results may not be what you expect though because malloc won't call constructors.
Try this:
std::string testString1("babab");
std::string testString2("12345678");
std::string testString3;
std::cout <<" sizeof(testString1)" <<sizeof(testString1) << std::endl;
std::cout <<" sizeof(testString2)" <<sizeof(testString2) << std::endl;
std::cout <<" sizeof(testString3)" <<sizeof(testString3) << std::endl;
On my machine this gives me the following output:
sizeof(testString1)8
sizeof(testString2)8
sizeof(testString3)8
Also is there some reason you are not using:
run_male_walker_struct_t *p = new(struct run_male_walker_struct);
This is the correct way to do it in c++, using malloc is almost certainly a mistake.
EDIT: see this page for a more detailed explanation of new vs malloc in c++:
http://www.codeproject.com/KB/tips/newandmalloc.aspx
The answer depends on what you mean by a "C struct".
If you mean "a struct that is valid under the C language", then the answer is obviously: it contains a datatype that isn't valid C, and so the struct itself isn't valid either.
If you mean a C++ POD type, then the answer is no, it is not illegal, but the struct is no longer a POD type (because in order to be POD, all its members must be POD as well, and std::string isn't)
Today I stumbled over a piece of code that looked horrifying to me. The pieces was chattered in different files, I have tried write the gist of it in a simple test case below. The code base is routinely scanned with FlexeLint on a daily basis, but this construct has been laying in the code since 2004.
The thing is that a function implemented with a parameter passing using references is called as a function with a parameter passing using pointers...due to a function cast. The construct has worked since 2004 on Irix and now when porting it actually do work on Linux/gcc too.
My question now. Is this a construct one can trust? I can understand if compiler constructors implement the reference passing as it was a pointer, but is it reliable? Are there hidden risks?
Should I change the fref(..) to use pointers and risk braking anything in the process?
What do you think?
Edit
In the actual code both fptr(..) and fref(..) use the same struct - changed code below to reflect this better.
#include <iostream>
#include <string.h>
using namespace std;
// ----------------------------------------
// This will be passed as a reference in fref(..)
struct string_struct {
char str[256];
};
// ----------------------------------------
// Using pointer here!
void fptr(string_struct *str)
{
cout << "fptr: " << str->str << endl;
}
// ----------------------------------------
// Using reference here!
void fref(string_struct &str)
{
cout << "fref: " << str.str << endl;
}
// ----------------------------------------
// Cast to f(const char*) and call with pointer
void ftest(void (*fin)())
{
string_struct str;
void (*fcall)(void*) = (void(*)(void*))fin;
strcpy(str.str, "Hello!");
fcall(&str);
}
// ----------------------------------------
// Let's go for a test
int main() {
ftest((void (*)())fptr); // test with fptr that's using pointer
ftest((void (*)())fref); // test with fref that's using reference
return 0;
}
What to you think?
Clean it up. That's undefined behavior and thus a bomb which might blow up anytime. A new platform or compiler version (or moon phase, for that matter) could trip it.
Of course, I don't know what the real code looks like, but from your simplified version it seems that the easiest way would be to give string_struct an implicit constructor taking a const char*, templatize ftest() on the function pointer argument, and remove all the casts involved.
It's obviously a horrible technique, and formally it's undefined behaviour and a serious error to call a function through an incompatible type, but it should "work" in practice on a normal system.
At the machine level, a reference and a pointer have exactly the same representation; they are both just the address of something. I would fully expect that fptr and fref compile to exactly the same thing, instruction for instruction, on any computer you could get your hands on. A reference in this context can simply be thought of as syntactic sugar; a pointer that is auto-dereferenced for you. At the machine level they are exactly the same. Obviously there might be some obscure and/or defunct platforms where that might not be the case, but generally speaking that's true 99% of the time.
Furthermore, on most common platforms, all object pointers have the same representation, as do all function pointers. What you've done really isn't all that different from calling a function expecting an int through a type taking a long, on a platform where those types have the same width. It's formally illegal, and all but guaranteed to work.
It can even be inferred from the definition of malloc that all object pointers have the same representation; I can malloc a huge chunk of memory, and stick any (C-style) object I like there. Since malloc only returned one value, but that memory can be reused for any object type I like, it's hard to see how different object pointers could reasonably use different representations, unless the compiler was maintaining an big set of value-representation mappings for every possible type.
void *p = malloc(100000);
foo *f = (foo*)p; *f = some_foo;
bar *b = (bar*)p; *b = some_bar;
baz *z = (baz*)p; *z = some_baz;
quux *q = (quux*)p; *q = some_quux;
(The ugly casts are necessary in C++). The above is required to work. So while I don't think it is formally required that afterwards memcmp(f, b) == memcmp(z, q) == memcmp(f, q) == 0, but it's hard to imagine a sane implementation that could make those false.
That being said, don't do this!
It works by pure chance.
fptr expects a const char * while fref expects a string_struct &.
The struct string_struct have the same memory layout as the const char * since it only contains a 256 bytes char array, and does not have any virtual members.
In c++, call by reference e.g. string_struct & is implemented by passing a hidden pointer to the reference so on the call stack it will be the same as if it was passed as a true pointer.
But if the structure string_struct changes, everything will break so the code is not considered safe at all. Also it is dependent on compiler implementation.
Let's just agree that this is very ugly and you're going to change that code.
With the cast you promise that you make sure the types match and they clearly don't.
At least get rid of the C-style cast.
I've recently come across this rant.
I don't quite understand a few of the points mentioned in the article:
The author mentions the small annoyance of delete vs delete[], but seems to argue that it is actually necessary (for the compiler), without ever offering a solution. Did I miss something?
In the section 'Specialized allocators', in function f(), it seems the problems can be solved with replacing the allocations with: (omitting alignment)
// if you're going to the trouble to implement an entire Arena for memory,
// making an arena_ptr won't be much work. basically the same as an auto_ptr,
// except that it knows which arena to deallocate from when destructed.
arena_ptr<char> string(a); string.allocate(80);
// or: arena_ptr<char> string; string.allocate(a, 80);
arena_ptr<int> intp(a); intp.allocate();
// or: arena_ptr<int> intp; intp.allocate(a);
arena_ptr<foo> fp(a); fp.allocate();
// or: arena_ptr<foo>; fp.allocate(a);
// use templates in 'arena.allocate(...)' to determine that foo has
// a constructor which needs to be called. do something similar
// for destructors in '~arena_ptr()'.
In 'Dangers of overloading ::operator new[]', the author tries to do a new(p) obj[10]. Why not this instead (far less ambiguous):
obj *p = (obj *)special_malloc(sizeof(obj[10]));
for(int i = 0; i < 10; ++i, ++p)
new(p) obj;
'Debugging memory allocation in C++'. Can't argue here.
The entire article seems to revolve around classes with significant constructors and destructors located in a custom memory management scheme. While that could be useful, and I can't argue with it, it's pretty limited in commonality.
Basically, we have placement new and per-class allocators -- what problems can't be solved with these approaches?
Also, in case I'm just thick-skulled and crazy, in your ideal C++, what would replace operator new? Invent syntax as necessary -- what would be ideal, simply to help me understand these problems better.
Well, the ideal would probably be to not need delete of any kind. Have a garbage-collected environment, let the programmer avoid the whole problem.
The complaints in the rant seem to come down to
"I liked the way malloc does it"
"I don't like being forced to explicitly create objects of a known type"
He's right about the annoying fact that you have to implement both new and new[], but you're forced into that by Stroustrups' desire to maintain the core of C's semantics. Since you can't tell a pointer from an array, you have to tell the compiler yourself. You could fix that, but doing so would mean changing the semantics of the C part of the language radically; you could no longer make use of the identity
*(a+i) == a[i]
which would break a very large subset of all C code.
So, you could have a language which
implements a more complicated notion of an array, and eliminates the wonders of pointer arithmetic, implementing arrays with dope vectors or something similar.
is garbage collected, so you don't need your own delete discipline.
Which is to say, you could download Java. You could then extend that by changing the language so it
isn't strongly typed, so type checking the void * upcast is eliminated,
...but that means that you can write code that transforms a Foo into a Bar without the compiler seeing it. This would also enable ducktyping, if you want it.
The thing is, once you've done those things, you've got Python or Ruby with a C-ish syntax.
I've been writing C++ since Stroustrup sent out tapes of cfront 1.0; a lot of the history involved in C++ as it is now comes out of the desire to have an OO language that could fit into the C world. There were plenty of other, more satisfying, languages that came out around the same time, like Eiffel. C++ seems to have won. I suspect that it won because it could fit into the C world.
The rant, IMHO, is very misleading and it seems to me that the author does understand the finer details, it's just that he appears to want to mislead. IMHO, the key point that shows the flaw in argument is the following:
void* operator new(std::size_t size, void* ptr) throw();
The standard defines that the above function has the following properties:
Returns: ptr.
Notes: Intentionally performs no other action.
To restate that - this function intentionally performs no other action. This is very important, as it is the key to what placement new does: It is used to call the constructor for the object, and that's all it does. Notice explicitly that the size parameter is not even mentioned.
For those without time, to summarise my point: everything that 'malloc' does in C can be done in C++ using "::operator new". The only difference is that if you have non aggregate types, ie. types that need to have their destructors and constructors called, then you need to call those constructor and destructors. Such types do not explicitly exist in C, and so using the argument that "malloc does it better" is not valid. If you have a struct in 'C' that has a special "initializeMe" function which must be called with a corresponding "destroyMe" then all points made by the author apply equally to that struct as they do to a non-aggregate C++ struct.
Taking some of his points explicitly:
To implement multiple inheritance, the compiler must actually change the values of pointers during some casts. It can't know which value you eventually want when converting to a void * ... Thus, no ordinary function can perform the role of malloc in C++--there is no suitable return type.
This is not correct, again ::operator new performs the role of malloc:
class A1 { };
class A2 { };
class B : public A1, public A2 { };
void foo () {
void * v = ::operator new (sizeof (B));
B * b = new (v) B(); // Placement new calls the constructor for B.
delete v;
v = ::operator new (sizeof(int));
int * i = reinterpret_cast <int*> (v);
delete v'
}
As I mention above, we need placement new to call the constructor for B. In the case of 'i' we can cast from void* to int* without a problem, although again using placement new would improve type checking.
Another point he makes is about alignment requirements:
Memory returned by new char[...] will not necessarily meet the alignment requirements of a struct intlist.
The standard under 3.7.3.1/2 says:
The pointer returned shall be suitably aligned so that it can be converted to a
pointer of any complete object type and then used to access the object or array in the storage allocated (until
the storage is explicitly deallocated by a call to a corresponding deallocation function).
That to me appears pretty clear.
Under specialized allocators the author describes potential problems that you might have, eg. you need to use the allocator as an argument to any types which allocate memory themselves and the constructed objects will need to have their destructors called explicitly. Again, how is this different to passing the allocator object through to an "initalizeMe" call for a C struct?
Regarding calling the destructor, in C++ you can easily create a special kind of smart pointer, let's call it "placement_pointer" which we can define to call the destructor explicitly when it goes out of scope. As a result we could have:
template <typename T>
class placement_pointer {
// ...
~placement_pointer() {
if (*count == 0) {
m_b->~T();
}
}
// ...
T * m_b;
};
void
f ()
{
arena a;
// ...
foo *fp = new (a) foo; // must be destroyed
// ...
fp->~foo ();
placement_pointer<foo> pfp = new (a) foo; // automatically !!destructed!!
// ...
}
The last point I want to comment on is the following:
g++ comes with a "placement" operator new[] defined as follows:
inline void *
operator new[](size_t, void *place)
{
return place;
}
As noted above, not just implemented this way - but it is required to be so by the standard.
Let obj be a class with a destructor. Suppose you have sizeof (obj[10]) bytes of memory somewhere and would like to construct 10 objects of type obj at that location. (C++ defines sizeof (obj[10]) to be 10 * sizeof (obj).) Can you do so with this placement operator new[]? For example, the following code would seem to do so:
obj *
f ()
{
void *p = special_malloc (sizeof (obj[10]));
return new (p) obj[10]; // Serious trouble...
}
Unfortunately, this code is incorrect. In general, there is no guarantee that the size_t argument passed to operator new[] really corresponds to the size of the array being allocated.
But as he highlights by supplying the definition, the size argument is not used in the allocation function. The allocation function does nothing - and so the only affect of the above placement expression is to call the constructor for the 10 array elements as you would expect.
There are other issues with this code, but not the one the author listed.