Reputation:
foo.h
#include <iostream>
#include <memory>
class Bar
{
public:
Bar() {};
~Bar() {};
void print() {
std::cout << "hello";
}
};
class Foo
{
public:
Foo();
~Foo();
void use() {
pteste->print();
}
private:
std::unique_ptr<Bar> pteste;
};
#endif
main.cpp
#include <memory>
#include "foo.h"
int main(int argc, char *argv[])
{
Foo s;
s.use();
return 0;
}
Why and how does it works "normally"?
Thanks
EDIT: I understand about the incomplete types, but what happens when I can use unique_ptr without using new and why works
EDIT2: Organized the code better for my question
Upvotes: 4
Views: 1825
Reputation: 1690
Short answer: It doesn't work.
This reference says that the default constructor of std::unique_ptr
creates an empty unique pointer, meaning it has no associated object.
The reason why this code prints hello
is because this statement
std::cout << "hello";
doesn't need anything of Bar
. It could just as well be a static method. Maybe the compiler inlines the function and replaces s.use()
with the std::cout
-statement. But even if it does call the method, you won't notice any errors since it doesn't access the memory of Bar
at all.
Make a slight change to your class and you will see what I mean:
class Bar
{
public:
Bar() : data(10) {};
~Bar() {};
void print() {
std::cout << "hello, data is: " << data;
}
int data;
};
Now, print
accesses invalid memory, because you never called new
(or even better: make_unique
). It may even work and print something to the console, but the output of data
will be garbage. If you're lucky, the application will crash.
Another reason why it appears to work (thanks Stas):
std::unique_ptr
defines operator->
, which simply returns the contained pointer, but does not check if the pointer points to valid memory. So pteste->
won't throw an exception.
Upvotes: 3
Reputation: 11761
Yes, this code will "normally" print "hello" to console and it is not related to unique_ptr
. You can replace std::unique_ptr<Bar> pteste
with Bar* pteste
in Bar
and get the same result.
Consider how pteste->print()
is called.
You can think about Bar::print()
as a free function that take pointer to Bar
object:
void print(Bar* this) {
std::cout << "hello";
}
See, pointer passed to print(Bar*)
is never touched, so you can theoretically pass whatever you want (null, garbage etc.) and it will print "hello" to console.
Upvotes: 1
Reputation: 4232
All pointers are implemented the same way. Even though you have pointers to different types, all are the size of an int usually. So the compiler does not need to know about the type of the pointee when it compiles your code. Now if you were to dereference that pointer that would be a different story. Even if you would initialize your unique_ptr
it would need to know the type, since new
needs to see the constructor.
Upvotes: 0
Reputation: 36082
It works because
std::unique_ptr<Bar> pteste;
is a pointer declaration to the instance, it does not instantiate the pointer so it does not need to know at this point the details about Bar (e.g. ctor).
In the case of
Bar pteste
in order for pteste to be constructed it will need the know definition but since Bar is only forward declared it will give an error.
Upvotes: 0