I am transiting from C pointers to C++ ones,now learning about auto_ptr. Here is the program I tried:
#include <iostream>
#include <memory>
#include "Car.h"
using namespace std;
typedef auto_ptr<Car> CarPtr;
int main() {
CarPtr au_ptr1(new Car());
CarPtr au_ptr2 = new Car();
Car *norm_ptr1 = new Car();
Car *norm_ptr2(new Car());
int *i_ptr1=new int();
int *i_ptr2(new int());
}
Statements like the following mean what?
int *i_ptr2(new int());
Car *norm_ptr2(new Car());
The statement mentioned above compiled successfully.And the following one,throws a compilation error - CarPtr au_ptr2 = new Car();
Why is that?
Thanks in Advance
It does have a copy constructor, but the conversion constructor is explicit, which is what is causing the error:
explicit auto_ptr (X* p=0) throw();
Which means a Car* can't be implicitly converted to a auto_ptr<Car>, which is what
CarPtr au_ptr2 = new Car();
attempts to do. This is called copy-initialization, as opposed to:
CarPtr au_ptr1 (new Car());
which is value-initialization. The first version will attempt to create a temporary CarPtr from a Car* and use it to initialize au_ptr2. The second one calls the copy constructor directly.
Statements like
int *i_ptr2(new int());
simply value-initialize the pointer with the value in the parenthesis.
Typically objects have copy constructor and pointers are not objects so they don't have copy constructor (or assignment operator or destructor). More precisely, pointers rely on the default copying mechanism.
When you talk about auto_ptr or any other smart pointers, they are just pointer namesakes. But in actual they are templatized objects which use the RAII mechanism.
CarPtr au_ptr2 = new Car(); // this is initialization not assignment
gives compilation error because corresponding CarPtr::CarPtr(...) constructor is made explicit, so it doesn't accept the = style initialization.
Raw pointers don't have constructors, but for most purposes they can be used as if they do. The built-in types can all be initialized from a value of any type convertible to their type, just like a user-defined class with a copy constructor can.
int *i_ptr1=new int();
int *i_ptr2(new int());
mean the same thing.
I think the reason for this is basically templates: it means that you can use a type T as though it were a user-defined type, and write T t(0); or T(0) or T(), and when T happens to be a built-in type the meaning is exactly the same as T t = 0; or (T)0 or (T)0 (again). Actually, the meaning of T(0) is by definition the same as (T)0 regardless of what constructors T has, but people who tell you not to use C-style casts in C++ code try to ignore that fact ;-)
auto_ptr does in fact have a copy constructor, but unlike most copy ctors it takes a non-const parameter, and modifies its argument. That's why in C++11 it's deprecated in favor of unique_ptr, which doesn't have a copy constructor but does have a move constructor.
As Luchian says, the problem with CarPtr au_ptr2 = new Car(); isn't (just) the copy constructor, it's also the lack of an implicit conversion from the type of new Car();, Car*, to auto_ptr<Car>. Copy initialization tries to implicitly convert the RHS to the type of the LHS, and then copy it to the LHS. Both of those fail in this example. Direct initialization is allowed to use explicit conversions and doesn't need a copy, so it succeeds.
One way in which built-in types don't behave as if they have constructors is default initialization. You can write:
int i = int();
and i is guaranteed initialized to zero. So you might imagine that it has a no-args constructor that sets it to zero. But if int were really a class type with that constructor, then writing:
int i;
would also guarantee i is zero, and it doesn't (at least, not in function scope).
By the way, don't get too excited by all this and accidentally invoke the so-called "most vexing parse".
int i();
is equivalent to int i(void);, not int i(0);. It declares a function, not an integer variable.
Related
I am trying to understand why
std::unique_ptr<MyClass> p = new MyClass;
Does not work, but
std::unique_ptr<MyClass> p;
p.reset(new MyClass);
is fine. I somewhat understand how they are different, but I would like to know why the choice was made to make them different. What is the danger in assignment not being the same as reset?
Firstly, std::unique_ptr<MyClass> p = new MyClass; is not assignment, it is copy initialization. And it doesn't work because the constructor of std::unique taking a raw pointer is marked as explicit:
explicit unique_ptr( pointer p ) noexcept;
It is declared as explicit to avoid unexpected (might be dangerous) implicit conversions, eg:
void foo(std::unique_ptr<int> uptr);
int *rptr = new int;
foo(rptr); // suppose rptr is implicitly converted to std::unique_ptr<int>
// then the ownership is passed to the parameter uptr
// when foo() returns uptr is destroyed; the pointer managed by it is deleted too
// since rptr has been deleted continue to deference on it leads to UB
*rptr = 42; // UB
Note that explicit constructors are not considered in copy initialization (eg std::unique_ptr<MyClass> p = new MyClass;). You can use them in direct initialization instead (eg std::unique_ptr<MyClass> p (new MyClass);). They are used to prohibit implicit conversions, but you can perform explicit conversions. Like the usage of reset, you have to do these things explicitly, to show (and make yourself) that you're pretty sure about what you're doing.
BTW: The assignment from raw pointer doesn't work either, because std::unique_ptr doesn't have an overloaded assignment operator taking a raw pointer as parameter. For the reason above, raw pointer can't be implicitly converted to std::unique_ptr, so the move assignment operator (which takes std::unique_ptr as parameter) won't be considered either.
I am trying to understand why
std::unique_ptr<MyClass> p = new MyClass;
does not work
The same reason as #songyuanyao mentioned, where it's declared explicit, tells that you can still initialize it in a different form of initialization that surpasses explicit:
// Valid, since now it's 'explicit'
std::unique_ptr<MyClass> p { new MyClass{} };
I was reading through this book
C++ standard library book
And here is the part i can not understand:
Note that class auto_ptr<> does not allow you to initialize an object with an ordinary pointer by
using the assignment syntax.
std::auto_ptr<ClassA> ptr1(new ClassA); //ok
std::auto_ptr<ClassA> ptr2 = new ClassA; //error
I don't understand why it is not allowed. What kind of pitfalls they were trying to avoid by not allowing initialization with assignment syntax
The fact that the assignment syntax cannot be used to initialize an auto_ptr from a raw pointer is a side effect of the constructor which takes a raw pointer being marked explicit. And the usual reason to mark a constructor as explicit is to prevent things like this:
void take_ownership(std::auto_ptr<ClassA> ptr) {
// the pointer is deleted when this function ends
}
void foo() {
ClassA obj;
take_ownership(&obj); // oops, delete will be called on a pointer to
// an object which was not allocated with new
}
The call to the take_ownership function is an error there, because of the explicit classifier on the std::auto_ptr constructor. Instead, you have to deliberately construct an auto_ptr and pass that to the function.
void foo() {
std::auto_ptr<ClassA> ptr(new ClassA);
take_ownership(ptr); // okay
}
Of course this is not completely impervious to abuse (you can still pass a non-newed object to the constructor of auto_ptr), it is at least easier to spot when an abuse is taking place.
By the way, std::auto_ptr is deprecated. It is a very broken class (due to limitations in the language at the time it was introduced). Use std::unique_ptr instead.
Here is how std::auto_ptr defined:
template< class T > class auto_ptr;
template<> class auto_ptr<void>;
Hence auto_ptr is a class type. Let's see its constructors:
explicit auto_ptr( X* p = 0 );
auto_ptr( auto_ptr& r );
template< class Y >
auto_ptr( auto_ptr<Y>& r );
template< class Y >
auto_ptr( auto_ptr_ref<Y> m );
Consider the first constructor. we can use a pointer to X type object as parameter to call this constructor:
std::auto_ptr<X> ptr1(new X); //ok
In the meanwhile, this first constructor is explicit, hence we cannot use a pointer to X type object implicitly to transform to auto_ptr<X>. In other words, we cannot initialize directly it via a pointer to X type object.
std::auto_ptr<X> ptr1 = new X; //error; cannot implicitly transform
I don't understand why it is not allowed.
At first direct initialization and copy initialization are not the same thing.
std::auto_ptr<ClassA> ptr1(new ClassA); //ok
This is direct initialization.
std::auto_ptr<ClassA> ptr2 = new ClassA; //error
This is copy initialization.
Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.
So if you want to initialize std::auto_ptr with raw pointer via copy initialization, converting constructor will be needed, but std::auto_ptr doesn't have it.
std::auto_ptr's constructor taking one raw pointer as parameter is explicit, implicit conversion is prohibited.
What kind of pitfalls they were trying to avoid by not allowing initialization with assignment syntax
Consider about the following code if implicit conversion is allowed:
void f1(ClassA* p) { ... }
void f2(std::auto_ptr<ClassA> p) { ... }
...
ClassA* p = new ClassA;
f2(p); // call the wrong function, ownership is transfered to auto_ptr implicitly
p->something(); // UB, p has been deleted
delete p; // UB
#include<iostream>
using namespace std;
class A {
public:
int i;
};
int main() {
const A aa; //This is wrong, I can't compile it! The implicitly-defined constructor does not initialize ‘int A::i’
}
when I use
class A {
public:
A() {}
int i;
};
this is ok! I can compile it! why I can't compile it when I use the implicitly-defined constructor?
why the implicit-defined constructor does not work?
It does work, but one of the language rules is that it can't be used to initialise a const object unless it initialises all the members; and it doesn't initialise members with trivial types like int. That usually makes sense, since being const there's no way to give them a value later.
(That's a slight simplification; see the comments for chapter and verse from the language standard.)
If you define your own constructor, then you're saying that you know what you're doing and don't want that member initialised. The compiler will let you use that even for a const object.
If you want to set it to zero, then you could value-initialise the object:
const A aa {}; // C++11 or later
const A aa = A(); // historic C++
If you want to set it to another value, or set it to zero without the user having to specify value-initialisation, then you'll need a constructor that initialises the member:
A() : i(whatever) {}
why the implicit-defined constructor does not work?
Because the C++ standard says so:
[dcl.init] paragraph 7:
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
This ensures that you don't create a const object containing uninitialized data that cannot be initialized later.
To initialize a const-qualified object you need to have a user-provided default constructor or use an initialiser:
const A aa = A();
Here the object aa is initialized with the expression A() which is a value-initialized object. You can value-initialize a class type without a default constructor, because value-initialization will set values to zero if there is no default constructor for the type.
However, the rule in the standard is too strict, as it forbids using implicitly-defined constructors even when there are no data members or all data members have sensible default constructors, so there is a defect report against the standard proposing to change it, see issue 253.
You don't state what compiler you're using. I've tried this with VS2012 and get a warning C4269.
The reason this is a problem is because aa is const. Because you haven't defined a constructor a default one is used and so i can be anything. It also cannot be changed (because aa is const).
If you define a constructor, it is assumed that you are happy with the initialization of i. Although, in this case you haven't actually changed the behaviour.
From this MSDN page
Since this instance of the class is generated on the stack, the initial value of m_data can be anything. Also, since it is a const instance, the value of m_data can never be changed.
Because i is not initialized.
class A
{
public:
A()
{
i =0;
}
int i;
};
"Implicit constructor" means a constructor generated for you automatically and generates an error because it realizes it is not able to initialize the value of i. This can be a no-args constructor, a copy constructor or (as of C++11) a move constructor.
why the implicit-defined constructor does not work?
It works just fine, but it does not decide what your default values are implicitly (and as such, it only calls default constructors for it's members, but not for POD types).
If you want the constructor to initialize your members with certain values you have to explicitly write that (i.e. add a default constructor explicitly).
Making a default (implicit) constructor initialize POD members with a chosen value (like zero for example) would add extra computing cycles (and slow your program down) when you don't need that. C++ is designed to behave as if you (the programmer) know what you are doing (i.e. if you do not initialize your members explicitly, the compiler assumes you don't care what default value you get).
What is the difference between this:
TestClass t;
And this:
TestClass t = TestClass();
I expected that the second might call the constructor twice and then operator=, but instead it calls the constructor exactly once, just like the first.
TestClass t;
calls the default constructor.
TestClass t = TestClass();
is a copy initialization. It will call the default constructor for TestClass() and then the copy constructor (theoretically, copying is subject to copy elision). No assignment takes place here.
There's also the notion of direct initialization:
TestClass t(TestClass());
If you want to use the assignment operator:
TestClass t;
TestClass s;
t = s;
The first case is quite simple - constructs an instance using the default constructor.
The second class is Constructing an anonymous object and then calling the copy constructor. Notice that here the = is not assignment, it's similar to (but not identical) writing:
TestClass t(TestClass());
We can verify that this needs the copy constructor to be available by making it unavailable, e.g.:
#include <iostream>
struct TestClass {
TestClass() { std::cout << "Ctor" << std::endl; }
TestClass(const TestClass&) = delete;
};
int main() {
TestClass t = TestClass();
}
Which fails to compile because of the deleted copy constructor. (In C++03 you can use private: instead).
What's actually happening most likely though is that your compiler is doing Return value optimisation, whereby it's allowed to ommit the call to the copy constructor entirely provided a suitable one exists and would be accessible.
In the first one, you are calling the default constructor implicitly. And in the second one you're calling it explicitly.
The latter one could call copy constructor and thus requires one to be public.
Edit: I certainly drew far too big conclusions from the type name you used. The sentence above only applies for class-types (i.e. not POD). For POD types, the former leaves the variable uninitialized, while the latter initializes it with so-called "default" value.
boost::shared_array<char const *> x(new char const *[n]);
In the line above (n is integer number not greater than 100) I'm creating char const**(const char**) and putting it to smart pointer x for arrays to be deleted when x is deleted. And for me it is clearly how and why this work.
boost::shared_array<char const *> x = new char const *[n];
Now lets take a look to second line. Here in my opinion we do exactly the same as in first case. Yes at first glance we may seem that here we constructing x via NULL(default value of shared_array constructors parameter) then calling operator=, but this is mistake, and as I know in this case instead of operator= will be called constructor with pointer created by new opeartor.
But in spit of this I'm getting error C2440: 'initializing' : cannot convert from 'const char **' to 'boost::shared_array<T>
The only problem I see this is the explicit constructor of boost::shared_array<T>. But I don't know what is the problem? Why does explicit constructor cause this error? And if the problem is not in explicit constructor, then where, why?
Your guess is correct.
What you're trying to do in the second line is implicitly calling the constructor: you want the C++ compiler to realize that there is a constructor available that accepts a T* and use it. However, since the constructor is marked as explicit, it cannot be invoked this way.
See for example the discussion at http://www.go4expert.com/forums/showthread.php?t=20756.
Yes, the "problem" is that the T* constructor for shared_array is explicit. That forbids constructing with =.
Now lets take a look to second line. Here in my opinion we do exactly
the same as in first case. Yes at
first glance we may seem that here we
constructing x via NULL(default value
of shared_array constructors
parameter) then calling operator=, but
this is mistake, and as I know in this
case instead of operator= will be
called constructor with pointer
created by new opeartor.
That's not entirely true. Here's what actually happens in the general case. Suppose you have
struct A
{
/*explicit*/ A(int){}
};
A a = 7;
This isn't actually equivalent to A a(7). In the A a = 7 initialization, you call 2 constructors, the constructor that takes int to create a temporary, and the copy constructor to initialize a. Of course this is redundant in most cases and the compiler is allowed to omit the copy-constructor call (that is explicitly mentioned in the standard) but nonetheless it requires you to have one regardless of whether it decides to omit the call or not.
struct A
{
/*explicit*/ A(int){}
private: A(A const &){}
};
A a = 7;
Now this will be a compile-time error. If you add some tracing messages to both constructors, you'll most likely see that the copy constructor doesn't get called anyway, but that doesn't matter - it must be there and accessible.
As to why explicit hinders you to call the above syntax must be clear now - because that constructor is called implicitly to initialize the temporary, not a.
HTH and Cheers, :)