Passing smart pointer to function through non smart pointer argument - c++

Consider the following function:
void example(void **ptr){
std::cout << *ptr << "\n"; // pointer value
std::cout << ptr << "\n"; // pointer address
std::cout << **reinterpret_cast<int**>(ptr); // value
}
The function signature cannot be changed.
Is the following code valid, or should I use raw pointers?
int main()
{
std::unique_ptr<int> x = std::make_unique<int>(20);
std::cout << x.get() << "\n"; // pointer value
std::cout << &x << "\n"; // pointer address
example(reinterpret_cast<void**>(&x));
}
Live sample

It is not valid and has undefined behaviour.
(If it appears to work, it's because your particular implementation happens to store the unique_ptr's underlying pointer as its first member in this particular situation. It is still 100% pure crystal-clear undefined behaviour, though.)
If you can't change the function, you need to pass it something that actually is an int** - &x is a std::unique_ptr<int>*, and no amount of casting can make a valid int** from it.
(I have found that it's often a mistake to think of the smart "pointers" as pointers.)
The way to get a pointer to the underlying object in a smart "pointer" is through get():
int* p = x.get();
and then you can pass a pointer to p (you still need to cast it, though):
example(reinterpret_cast<void**>(&p));

Generally only void * have special rights and privileges when it comes to playing funny games with casting. However, given the situation that you have to work with:
example(reinterpret_cast<void**>(&x));
Note that you're passing &x and laundering it with reinterpret_cast. In example() you should do exactly the opposite:
void example(void **ptr)
{
auto x_ptr = reinterpret_cast<std::unique_ptr<int> *>(ptr);
Now you have a std::unique_ptr<int> * to work with. You can proceed according to your original plans. If you wish, your next step can be:
auto &x=*xptr;
And this x is the same x as in your main, for all practical purposes (and it is).

Related

About conversion between pointers

What does actually "conversion between pointers" mean ? If I have something like type_1* x, type_2* y and call dynamic_cast<type_1*>(y) (the question is not about dynamic cast, I used it because it is a function that works well with pointers casting as far as I know) what will it return ? A pointer of type *type_1 that points to the same object as y (or NULL, of course) ? Is this true? Is there anything more I should know to understand these castings between pointers ?
In case of classes and multiple inheritance, casting pointers can change the value (address) of the pointer as well.
Observe:
#include <iostream>
class Foo {
int foo;
};
class Bar {
double bar;
};
class Baz: public Foo, public Bar {
short baz;
};
int main() {
Baz bz;
std::cout << &bz << " " << static_cast<Bar *>(&bz) << " " << static_cast<Foo *>(&bz) << std::endl;
}
That's because the instances of the parent types are at different offsets of the derived object, they can't both sit at the same address.
This is upcasting (child to parent) which is always legal and static_cast is enough, for downcasting (parent to child) the extra checking of dynamic_cast is needed.
What does actually "conversion between pointers" mean ?
It means a conversion from pointer of one type to another pointer type.
what will [dynamic_cast<type_1*>(y)] return ? A pointer of type *type_1 that points to the same object as y (or NULL, of course) ? Is this true?
That is true. The "same object" is correct in an Object Oriented sense. Although from strict C++ sense, the pointed objects of different types may be considered to be different objects.
Is there anything more I should know to understand these castings between pointers ?
Yes, this is not the full extent of the rules relating to pointers and conversions. I recommend reading the documentation thoroughly and possibly some C++ books, in order to understand pointers and conversions.
Some time ago I saw the code that controlled the first Apollo mission. It was all-assembly, the C, lat alone C++, did not exist at those days. We can imagine that in that assembly, all they had at their disposal was registers and raw memory that had to be managed manually.
In modern, non-assembly languages, including C/C++, the compiler frees you from the burden of manual memory management. The memory for named variables, like in
int x = 0;
is allocated and deallocated automatically. Also, you don't need to bother about memory that is needed temporarily, e.g., during some complex computations. In C/C++ these "usual" variables are accompanied by pointers that allow you to access this automatically or semi-automatically managed memory indirectly. Moreover, you need pointers to access the heap-allocated ("nameless") memory (remember operator new?). And this is almost all you should know about them. The syntax is known: the asterisk *, the "arrow" -> and the ampersand &. It's the compiler's duty to protect you from doing probably stupid operation on pointers, like using them as if they are just integers (a popular trick in 1970s) or using them as if they pointed to something else that you've declared. Declaring a pointer means that you declare the whole set of operations on it. To protect you from accidental shooting your foot off, the compiler forbids such horrible things like automatic assignments of pointers of different types.
However, you are the master of the compiler, not the opposite, so you can take the full control. This is where pointer conversion steps in. On the one hand, by ussing pointer conversion, you can temporarily switch off all the safeties the creators of the compiler has prepared for you. On the other hand, with a suitable cast, you can guarantee that your program is correct!
The really important question is: which operations involving pointer conversions are guaranteed to be safe?
The first, a bit surprising thing is: there are several types of pointers, which need not be mutually compatible:
pointer to data
pointer to (free) function
pointer to data member
pointer to member function
void* pointer
Their internal representation, or even the number of bits they occupy, need not be the same!
So, what is allowed?
The C language loves to use void* as the means of passing data of arbitrary type. So you can be sure that you can convert any data pointer to void* and then back to its original type and this will work. Similarly with pointers to functions.
Another place where pointer conversions are natural is low-level serialization. Many function in both C and C++ work on buffers declared as char* or const char*. From this you can infer that you should be able to convert any pointer to data to char* and it will work as expected.
The third common pointer conversion is when you deal with inheritance, especially in the context of classes with virtual functions. Here comes dynamic_cast to cast a pointer in the potentially "dangerous" way along the inheritance tree.
There are also other casts that are safe and others that may lead to undefined behavior or even segmentation fault. For example, pointers to int are often casts to or from pointers to float and it usually works, e.g., in AVX or GPU programming. Correctness of these particular casts are guaranteed by hardware vendors rather than the C++ standard.
What dangers are there if you leave the land of safe casts? For example, you cast a pointer to function to void* and then this pointer to char*. If you try to dereference this pointer, let alone write through it, your program is doomed to die immediately. Similarly dangerous conversions are between pointers to data and pointers to members. Don't do that. Also, don't use pointers to cast pointers to integers etc. This is generally the land of undefined behavior, unless you carefully consulted the standard and are sure the thing you're doing is safe.
To sum this long story up: pointer conversion usually generates no code, but sometimes it does. See the examples below.
EXAMPLES
1. Inheritance. Static cast vs. dynamic cast.
Suppose we have XY derived from both X and Y:
struct X
{
float x;
};
struct Y
{
float y;
};
struct XY : public X, public Y
{
float xy;
};
Then this code:
XY a;
std::cout << static_cast<X*>(&a) << "\n";
std::cout << static_cast<Y*>(&a) << "\n";
std::cout << reinterpret_cast<X*>(&a) << "\n";
std::cout << reinterpret_cast<Y*>(&a) << "\n";
std::cout << &a << "\n";
may produce these results:
0x7fffc7d75dcc
0x7fffc7d75dd0
0x7fffc7d75dcc
0x7fffc7d75dcc
0x7fffc7d75dcc
So, static_cast does change the "value" of the pointer! At the same time, reinterpret_cast only "reinterprets" the bits stored in a pointer variable / register, but does not modify them.
Interestingly, if we defined a virtual method in Y, the same code would reveal a reversed order of X and Y inside XY:
0x7ffc6a45764c
0x7ffc6a457640
0x7ffc6a457640
0x7ffc6a457640
0x7ffc6a457640
So, the order of embedding the base classes in multiple inheritance seems to be undefined / implementation-dependent.
2. casts to integer types
The standard allows to cast pointers to and from integer types of sufficient length.
Casts of "C-style pointers" to integer types work as expected:
XY a;
std::cout << std::hex << reinterpret_cast<uint64_t>(&a) << "\n";
std::cout << reinterpret_cast<void*>(&a) << "\n";
yields, for example,
7fff84f5a6cc
0x7fff84f5a6cc
as expected. But he standard does not guarantee this.
3. pointers to data members
This case is more interesting: pointers to members cannot be cast to anything. But there's a workaround:
XY a;
float XY::* mptr_xy = &XY::xy;
float XY::* mptr_x = &XY::x;
float XY::* mptr_y = &XY::y;
std::cout << *reinterpret_cast<uint64_t*>(&mptr_x) << "\n";
std::cout << *reinterpret_cast<uint64_t*>(&mptr_y) << "\n";
std::cout << *reinterpret_cast<uint64_t*>(&mptr_xy) << "\n";
This may give the following result:
0
4
8
This shows that the pointers to members are offsets into objects memory rather than pointers to RAM. If we add a virtual destructor to Y we may get this:
12
8
16
The offsets have changed, making room for vptr and moving Y to the front. If both X and Y have virtual destructors, a possible result is this:
8
24
28
Conclusion: pointers to members are completely different beasts than standard pointers.
4. Dynamic casts
Suppose both X and Y have virtual destructors. This code:
XY xy;
Y y;
std::cout << "xy:\n";
std::cout << &xy << "\n";
std::cout << dynamic_cast<Y*>(&xy) << "\n";
std::cout << dynamic_cast<Y*>(dynamic_cast<X*>(&xy)) << "\n";
std::cout << dynamic_cast<X*>(&xy) << "\n";
std::cout << "y:\n";
std::cout << &y << "\n";
std::cout << dynamic_cast<XY*>(&y) << "\n";
std::cout << dynamic_cast<Y*>(dynamic_cast<XY*>(&y)) << "\n";
std::cout << dynamic_cast<Y*>(reinterpret_cast<XY*>(&y)) << "\n";
std::cout << dynamic_cast<Y*>(static_cast<XY*>(&y)) << "\n";
may produce this output:
xy:
0x7ffc0a570590
0x7ffc0a5705a0
0x7ffc0a5705a0
0x7ffc0a570590
y:
0x7ffc0a570580
0
0
0x7ffc0a570590
0x7ffc0a570580
Casts of the address of xy may give 2 different results.
Casts of the address of y may give 3 different results.
Summary:
Pointer casts in C++ are far more complex than in C. The standard only guarantees behavior, but not the implementation. So, for example, a cast to an integer type might involve some bit manipulation - existing implementations don't do this because there's no point in doing this, not because it's forbidden. The main differences arise from C++ having multiple inheritance, virtual function and pointers to members.
Caveat. This long reply did not cover all aspects of pointer casts, most notably data alignment. Also, the standard does not impose any limitation on the implementation of various pointers, and even the reinterpret_cast is not guaranteed to preserve the internal bit representation:
Unlike static_cast, but like const_cast, the reinterpret_cast expression does not compile to any CPU instructions (except when converting between integers and pointers or on obscure architectures where pointer representation depends on its type).
https://en.cppreference.com/w/cpp/language/reinterpret_cast
See also:
https://en.cppreference.com/w/cpp/language/explicit_cast
https://en.cppreference.com/w/cpp/language/const_cast
https://en.cppreference.com/w/cpp/language/dynamic_cast
https://en.cppreference.com/w/cpp/language/static_cast
https://en.cppreference.com/w/cpp/language/implicit_conversion
what does casting between pointers mean ?
You change the meaning of the same data / object.
Example 1:
class Base {
public:
void food() {printf("foo\n");}
}
class Derived : public Base {
public:
void bar() {printf("bar");}
}
int main() {
Base * base = new Derived();
base->foo(); // OK, it compiles
base->bar(); // **NOK, syntax error**, even if you instantiated Derived class
Derived * derived = static_cast<Derived *>(base);
derived->foo(); // OK, it compiles
derived->bar(); // OK, it compiles
return 0;
}
Example 2:
int main() {
char word[] = {'W', 'o', 'r', 'd', '\0', '\0', '\0', '\0'}; // array of 8 chars
const char * ptrChar = static_cast<const char *>(word); // cast to "const char* "
const int * ptrInt = reinterpret_cast<const int *>(word); // cast to "const int* "
int lenChar = 0
while (ptrChar[lenChar] != 0)
++ lenChar;
printf("lenChar = %d\n", lenChar); // lenChar = 4
int lenInt = 0;
while (ptrInt[lenInt] != 0)
++ lenInt;
printf("lenInt = %d\n", lenInt); // lenInt = 1
return 0;
}
etc....
You should "play" more with pointers then you'll understand more about pointers and sense of conversion. It is a huge topic and essential in C/C++
dynamic casting is more complex. It requires understanding of run-time and compile-time, polymorphism, hierarchy, etc. In these 2 examples i didn't use dynamic casting because it would not work.

Unique pointer still holds the object after moving

I'm going through some tutorials on how smart pointers work in C++, but I'm stuck on the first one I tried: the unique pointer. I'm following guidelines from wikipedia, cppreference and cplusplus. I've also looked at this answer already. A unique pointer is supposed to be the only pointer that has ownership over a certain memory cell/block if I understood this correctly. This means that only the unique pointer (should) point to that cell and no other pointer. From wikipedia they use the following code as an example:
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; //Compile error.
std::unique_ptr<int> p3 = std::move(p1); //Transfers ownership. p3 now owns the memory and p1 is rendered invalid.
p3.reset(); //Deletes the memory.
p1.reset(); //Does nothing.
Until the second line, that worked fine for me when I test it. However, after moving the first unique pointer to a second unique pointer, I find that both pointers have access to the same object. I thought the whole idea was for the first pointer to be rendered useless so to speak? I expected a null pointer or some undetermined result. The code I ran:
class Figure {
public:
Figure() {}
void three() {
cout << "three" << endl;
}
};
class SubFig : public Figure {
public:
void printA() {
cout << "printed a" << endl;
}
};
int main()
{
unique_ptr<SubFig> testing (new SubFig());
testing->three();
unique_ptr<SubFig> testing2 = move(testing);
cout << "ok" << endl;
int t;
cin >> t; // used to halt execution so I can verify everything works up til here
testing->three(); // why is this not throwing a runtime error?
}
Here, testing has been moved to testing2, so I'm surprised to find I can still call the method three() on testing.
Also, calling reset() doesn't seem to delete the memory like it said it would. When I modify the main method to become:
int main()
{
unique_ptr<SubFig> testing (new SubFig());
testing->three();
unique_ptr<SubFig> testing2 = move(testing);
cout << "ok" << endl;
int t;
cin >> t;
testing.reset(); // normally this should have no effect since the pointer should be invalid, but I added it anyway
testing2.reset();
testing2->three();
}
Here I expect three() not to work for testing2 since the example from wikipedia mentioned the memory should be deleted by resetting. I'm still printing out printed a as if everything is fine. That seems weird to me.
So can anyone explain to me why:
moving from one unique pointer to another unique pointer doesn't make the first one invalid?
resetting does not actually remove the memory? What's actually happening when reset() is called?
Essentially you invoke a member function through a null pointer:
int main()
{
SubFig* testing = nullptr;
testing->three();
}
... which is undefined behavior.
From 20.8.1 Class template unique_ptr (N4296)
4 Additionally, u can, upon request, transfer ownership to another
unique pointer u2. Upon completion of such a transfer, the following
postconditions hold:
u2.p is equal to the pre-transfer u.p,
u.p is equal to nullptr, and
if the pre-transfer u.d maintained state, such state has been transferred to u2.d.
(emphasis mine)
After the std::move() the original pointer testing is set to nullptr.
The likely reason std::unique_ptr doesn't check for null access to throw a runtime error is that it would slow down every time you used the std::unique_ptr. By not having a runtime check the compiler is able to optimize the std::unique_ptr call away entirely, making it just as efficient as using a raw pointer.
The reason you didn't get a crash when calling the nullptr is likely because the function you called doesn't access the (non-existent) object's memory. But it is undefined behavior so anything could happen.
On calling std::unique_ptr<int> p3 = std::move(p1); your original pointer p1 is in undefined state, as such using it will result in undefined behavior. Simply stated, never ever do it.

c++ - pointers and references : a simple example

I am working through a c++ book/guide, but the pointers and references seem a little vague to me. This is coming from myself as a C# programmer and c# mindset that uses
foo(ref int x) and bar(out int y)
A small piece of code I wrote to show the memory position and memory position value, but I do not fully understand what each means, and in what context this is in.
int main()
{
int i = 50;
foo(&i);
cout << "\n\n" ;
foo(i);
getchar();
return 0;
}
void foo(int& bar) // & refer
{
cout << "refer";
cout << "\n&bar = " << &bar;
cout << "\nbar = " << bar;
}
void foo(int* bar)// * pointer
{
cout << "prefer";
cout << "\n&bar = " << &bar;
cout << "\nbar = " << bar;
}
output shows:
pointer
&bar = 0018FC30
bar = 0018FD04
refer
&bar = 0018FD04
bar = 50
What does & and * in each case mean, and how does it affect the output?
ofcourse, I have added all necessary methods into the .h file
UPDATE AFTER READING SOME ANSWERS
int i (values from pointer, aka points directly to variable)
has a value = 50
has an memory address = 0018FD04
pointer which points to int i
has a value which is int i memory address = 0018FD04
has its own memory address = 0018FC30
thus, to clarify, using a "refer" or "&bar" in the example actually creates a new variable which duplicates the int i passed through in the foo(int& bar).
Instead of the new &bar value containing the 50, it will have the memory address of the int i.
TL;DR
foo(int bar) receives the "value" of the variable
foo(int* bar) receives the "value" of the variable, if changed, it will change the variable in the calling method.
foo(int& bar) receives the pointer/memory address of the variable.
I hope others find this as useful as I did, thank you all!
Okay, the pointer first :
void foo(int* bar)// * pointer
{
cout << "prefer";
cout << "\n&bar = " << &bar;
cout << "\nbar = " << bar;
}
You're calling it with
int i = 50;
foo(&i);
In this context, *bar will be 50 (get the value the int pointer is pointing to).
&bar is the address of the pointer that is created ( the pointer itself needs a place in memory too of course ).
bar is the address of the object the pointer is pointing to ( i in this case )
Reference case:
void foo(int& bar) // & refer
{
cout << "refer";
cout << "\n&bar = " << &bar;
cout << "\nbar = " << bar;
}
Here, bar will refer to i that you created in main. &bar will return the address of i and bar it's value, 50 in this case. References themselves don't have their own addresses.
Coming from C#, pointers can be thought of like classes, while values act more like structs - when you alter their members, you're either altering a copy (in the case of pass-by-value objects, which is not shown), altering the same object you've been passed (in the case of pass-by-pointer), or altering the same value including the reference to the actual value you've been passed (in the case of pass-by-reference). In other words, instead of the object's declaration controlling how it's passed, the implementation decides how it's passed.
For each method header, behavior is as follows:
void foo(int& bar) // & refer
When you pass using type& name, you're passing by reference, and you're passing the equivalent of C#'s ref (or out if you didn't give the variable a meaningful value to begin with). Even though you've passed a value, changes to the value, including changing the value itself, will be reflected in the calling scope.
void foo(int* bar)// * pointer
When you pass an object using type* name, you're passing the pointer to that object. You are, however, passing the pointer as a value, so while you could change members (moot in the case of an int) you could not change the value (bar) itself. Again like C#, this would be like passing a class object without the use of ref/out - reassignment won't be reflected in the calling scope.
Going over the use of & in code...
cout << "\n&bar = " << &bar;
In this case, & the "address-of" operator. Whenever you take &variable in code, you're going to get the memory address of that variable - printing this value will give your the hexadecimal address the variable lives in RAM, which is basically gibberish in all but the most technical debug and sample cases. This also applies to pointers - you can take the pointer of a pointer (of a pointer of a pointer... ad infinitum).
In C++ the default is pass by value. So if you have a f(X x) then the compiler is going to generate code that copies the class X every time you call the function. If X is large (like an array, string, map or something like that) you probably don't want a copy, for performance reasons. Making a copy also means that if you modify the parameter inside your function you only modify the copy so it won't be visible after you return.
Now to make things visible outside of the function you must modify the instance of the caller. To do this you must have a pointer(*) or a reference(&) to that instance. The immediately visible difference between the two is that you can compare the pointer to nullptr (it's more tricky for the reference), that for the pointer you must use -> to access it's members, vs . for the reference and that when calling you must take the address of the argument to get the pointer (f(&global_x)). In general you should use references where there argument can never be nullptr. You should prefer pointer when it's reasonable to call the function with that argument missing. These are equivalent with C# ref or out. There are more subtle differences between the two but they only become apparent if you are writing templates and libraries.
If you want to avoid the copy but you don't want to modify the argument you should use const, which asks the compiler to make sure that you don't accidentally modify the value. You can use it with both references and pointers.
Picking references vs. pointers is most of the time a ting of coding style. The most important thing is to be consistent because it is annoying having to lookup function definitions all the time to see if you used pointers or references that one time.
I am working through a c++ book/guide, but the pointers and references seem a little vague to me
You'll find plenty of explanations for this if you try searching, but here is my attempt.
A pointer is a variable that tells you where to find another variable, by pointing to it. A pointer is really just a number that corresponds to the address in memory of another variable. Examples of addresses are the 0018FC30 and 0018FD04 in your output.
A reference is an alias (another name for) a variable. If you have a variable a and you create a reference b to it, b becomes an alternative name for a.
The value of a reference is not an address like for a pointer, it's the value of whatever it refers to.
The interest of references is that when you copy them or pass them around, you're not copying the actual value (which can be expensive), you're only copying the name.
The main observable difference is that you can change what a pointer points to (by changing the value of a pointer, since its value is really just an address), but you can't change what a reference refers to (in a way, it becomes what it refers to).
What does & and * in each case mean, and how does it affect the output?
When you have a variable a, the expression &a means "take the address of a".
This is interesting, because if you have the address of a variable, you can make a pointer point to it.
And *a could be seen as more or less the inverse operation, if you have a pointer a, *a means "take the value at the address this pointer points to".
You can use &a on anything that has an address. You could even take the address of a pointer to create a pointer-to-pointer! But you can only use *a on something that behaves like a pointer, it would be meaningless otherwise.
Imagine we have:
int* a; // This holds memory addresses
a = new int;
cout << a; // This will print the memory address of your new int
cout << *a; // This will print the value at the memory address a is holding
cout << &a; // This will print the memory address of the pointer variable.
If you declare a function that takes a pointer as a parameter, you should pass a pointer and not a static variable.
I hope this helped you even if its just a little bit.

Passing an address to a class and from there to its "child-class" by reference in C++

"pointer" holds the address of "Int". I want to pass that address to my given classes by reference:
class N {
public:
N(int &pPointer){
std::cout << "Address: " << &(pPointer) <<" \n";
}
};
class M {
public:
M(int &pPointer):n(pPointer) {
std::cout << "Address: " << &pPointer <<" \n";
}
private:
N n;
};
int main () {
int Int = 5;
int *pointer = &Int;
std::cout << "Address: " << pointer <<" \n";
M m(*pointer);
return 0;
}
Is this a good practice (since I'm kind of using a reference to a dereferenced pointer)?
Or is it absolutely horrible?
I simply want to avoid pointers here. (Although I'm forced to use "*pointer" in the beginning. )
It's totally OK to pass by reference instead of passing by pointer to use the abject later, as lon you know the pointer you are using is valid. This allows you to skip the nullptr check, because references can't be null.
However if you store a reference, you won't be able to replace the object being referred to, to another. But you can do that with a pointer.
You can, later, take the address of the referenced object, but do you really think you need to do that? Because you can use the reference instead
It's ok if you must, but do consider using a constant reference N(const int& pPointer) instead in the function parameters. This means you can't change the pointer to point at something else, but you could can still modify the underlying variable value with a dereference.
Also take care if you store the reference as a data member in the class. You could end up with a 'dangling reference' if the object in the caller (an int in your case) goes out of scope.

Nullptr and checking if a pointer points to a valid object

In a couple of my older code projects when I had never heard of smart pointers, whenever I needed to check whether the pointer still pointed to a valid object, I would always do something like this...
object * meh = new object;
if(meh)
meh->member;
Or when I needed to delete the object safely, something like this
if(meh)
{
delete meh;
meh = 0;
}
Well, now I have learned about the problems that can arise from using objects and pointers in boolean expressions both with literal numbers, the hard way :. And now I've also learned of the not so new but pretty cool feature of C++, the nullptr keyword. But now I'm curious.
I've already gone through and revised most of my code so that, for example, when deleting objects I now write
if(meh)
{
delete meh;
meh = nullptr;
}
Now I'm wondering about the boolean. When you pass just say an int into an if statement like this,
int meh;
if(meh)
Then it implicitly checks for zero without you needing to write it.
if(meh == 0) // does the exact same check
Now, will C++ do the same for pointers? If pass in a char * like this to an if statement?
char * meh;
if(meh)
Then will it implicitly compare it with nullptr? Because of how long I have been writing these ifs like this, it is second nature at this point to check if the pointers valid before using by typing if (object *) and then calling its members. If this is not the functionality why not? Too difficult to implement? Would solve some problems by removing yet another tiny way you could mess up your code.
In C, anything that's not 0 is true. So, you certainly can use:
if (ptrToObject)
ptrToObject->doSomething();
to safely dereference pointers.
C++11 changes the game a bit, nullptr_t is a type of which nullptr is an instance; the representation of nullptr_t is implementation specific. So a compiler may define nullptr_t however it wants. It need only make sure it can enforce proper restriction on the casting of a nullptr_t to different types--of which boolean is allowed--and make sure it can distinguish between a nullptr_t and 0.
So nullptr will be properly and implicitly cast to the boolean false so long as the compiler follows the C++11 language specification. And the above snippet still works.
If you delete a referenced object, nothing changes.
delete ptrToObject;
assert(ptrToObject);
ptrToObject = nullptr;
assert(!ptrToObject);
Because of how long I have been writing these ifs like this, it is second nature at this point to check if the pointers valid before using by typing if (object *) and then calling it's members.
No. Please maintain a proper graph of objects (preferably using unique/smart pointers). As pointed out, there's no way to determine if a pointer that is not nullptr points to a valid object or not. The onus is on you to maintain the lifecycle anyway.. this is why the pointer wrappers exist in the first place.
In fact, because the life-cycle of shared and weak pointers are well defined, they have syntactic sugar that lets you use them the way you want to use bare pointers, where valid pointers have a value and all others are nullptr:
Shared
#include <iostream>
#include <memory>
void report(std::shared_ptr<int> ptr)
{
if (ptr) {
std::cout << "*ptr=" << *ptr << "\n";
} else {
std::cout << "ptr is not a valid pointer.\n";
}
}
int main()
{
std::shared_ptr<int> ptr;
report(ptr);
ptr = std::make_shared<int>(7);
report(ptr);
}
Weak
#include <iostream>
#include <memory>
void observe(std::weak_ptr<int> weak)
{
if (auto observe = weak.lock()) {
std::cout << "\tobserve() able to lock weak_ptr<>, value=" << *observe << "\n";
} else {
std::cout << "\tobserve() unable to lock weak_ptr<>\n";
}
}
int main()
{
std::weak_ptr<int> weak;
std::cout << "weak_ptr<> not yet initialized\n";
observe(weak);
{
auto shared = std::make_shared<int>(42);
weak = shared;
std::cout << "weak_ptr<> initialized with shared_ptr.\n";
observe(weak);
}
std::cout << "shared_ptr<> has been destructed due to scope exit.\n";
observe(weak);
}
Now, will C++ do the same for pointers? If pass in a char * like this to an if statement?
So to answer the question: with bare pointers, no. With wrapped pointers, yes.
Wrap your pointers, folks.
It's not possible to test whether a pointer points to a valid object or not. If the pointer is not null but does not point to a valid object, then using the pointer causes undefined behaviour. To avoid this sort of error, the onus is on you to be careful with the lifetime of objects being pointed to; and the smart pointer classes help with this task.
If meh is a raw pointer then there is no difference whatsoever between if (meh) and if (meh != 0) and if (meh != nullptr). They all proceed iff the pointer is not null.
There is an implicit conversion from the literal 0 to nullptr .
It is always set a pointer to zero after invalidating it so that you know a pointer that's non-zero is valid" is an anti-pattern. What happens if you have two pointers to the same object? Setting one to zero won't be better and it does not affect the other.