I have a vector of class instances. Each of those instances has a unique_ptr to another class.
Since I never try to copy the class instance or even share the pointer, I felt like unique_ptr are more appropriate than shared_ptrs since the pointer is not shared, but only accessible through the class instance.
Is it bad practice? And why wouldn't this work? I understand that copying an instance to a unique pointer would be ill-formed, but since I move it, I do not understand why this would not be allowed?
Would I have to create a custom move constructor? And what should it do?
The unique ptr should be deleted as soon as the object instance is being removed from the vector as there are no references left, right?
Code Example for better understanding:
class A {
private:
int number;
public:
void f() {
std::cout << "The number is: " << number;
}
A(int i) : number{i} {}
~A() = default;
};
class B {
std::unique_ptr<A> good_a;
B() : good_a{ std::make_unique<A>(1) } {}
~B() = default;
};
int main()
{
std::vector<B> all;
B my_b(123);
all.emplace_back(std::move(my_b));
}
This answer focuses on compilation error you seem to be having. Bad or good practice is left for others to chime in.
Your code have several errors there, but the main one seems to be that your custom B( ) constructor inhibits default move constructor. If you add it, your code becomes well-formed.
Here is a full working code for the reference:
#include <memory>
#include <vector>
class A {
private:
int number;
public:
void f();
A(int i) : number{i} {}
~A() = default;
};
struct B {
std::unique_ptr<A> good_a;
B(int k) : good_a{ std::make_unique<A>(k) } {}
B(B&& ) = default;
B& operator=(B&& ) = default; // not needed for the example, but good for completeness
};
int main()
{
std::vector<B> all;
B my_b(123);
all.emplace_back(std::move(my_b));
}
And why wouldn't this work?
What you described could work.
I do not understand why this would not be allowed?
What you described would be allowed.
Would I have to create a custom move constructor?
No, that wouldn't be necessary, unless you define other special member functions, or have other members (beside the unique pointer) that have deleted or private move constructor.
The unique ptr should be deleted as soon as the object instance is being removed from the vector as there are no references left, right?
Members are destroyed when the super object is destroyed. And the destructor of the unique pointer invokes the deleter on its owned pointer.
Whether there are references to the pointed object has no effect on whether it is deleted or not. Anything referring to the deleted object will be left dangling.
Is it bad practice?
There isn't necessarily anything particularly bad about what you described in general, but that depends on exact details.
One potential issue is that dynamic allocation can be expensive in some cases, and using it unnecessarily would then be unnecessarily expensive. As such, you should to have some reason to allocate the pointed objects dynamically rather than storing them directly as members.
Bugs in your example:
You attempt to initialise B(123) but B has no constructor accepting an integer.
You attempt to initialise a B outside a member function of B, but its constructors and the destructor have private access.
You have user declared destructor for B, but no user declared move constructor or assignment operators and therefore the class isn't movable, which is a requirement for storing in std::vector.
Here is a fixed version that doesn't use unnecessary dynamic allocation:
struct A {
int number;
};
struct B {
A good_a;
};
B my_b{123};
all.push_back(my_b);
Please read this answear.
Depending what is explicitly declared respective constructors with default implementation are implicitly defined or dropped. Rules are described by this table:
Since you have used explicit defined destructor (as default) you have disabled ("not declared") move constructor.
So to fix it you have to explicitly define move constructor or remove definition of destructor: https://godbolt.org/z/dr8KrsTfq
Related
Have looked at various similar questions here but still can't figure out why the following code does not compile:
// these three are defined somewhere
class A;
std::unique_ptr<A> make_a();
void take_a(std::unique_ptr<A>&&);
int main(){
take_a(make_a()); // this fails
return 0;
}
According to this:
If the default deleter is used, T must be complete at the point in
code where the deleter is invoked, which happens in the destructor,
move assignment operator, and reset member function of
std::unique_ptr.
As far as I understand, none of these (destructor, move assignment operator, nor reset member function) happens in main.
So why does compiler needs the definition of A here?
Since main has a unique_ptr within its scope, realistically it would need to know how to delete the object it holds.
It's possible that take_a doesn't actually take ownership of the object, thus main would need to delete.
main gets a temporary unique_ptr from make_a(), let's call it X. It then passes an rvalue reference to X to take_a. It still has the destroy X. Hence, it has to call the destructor after take_a, even though X would typically be empty at that point.
We need class A description (at least constructor and destructor) in order to create and move std::unique_ptr<A> objects; take_a(make_a()) is creating such object because make_a() is pass-by-value. It first creates a new unique_ptr<A> object, initialize it (using default constructor), and then update its value and returns this new object. Here the default constructor/destructor is being used, which the compiler is unable to find.
#include <bits/stdc++.h>
class A {
public: A() {}
public: ~A() {}
};
std::unique_ptr<A> make_a() {
std::cout << "make";
std::unique_ptr <A> tt = nullptr;
return tt;
}
void take_a(std::unique_ptr<A>&&) {
std::cout << "take";
}
int main(){
take_a(make_a()); // this works now
return 0;
}
Edit: Forgot to add the link. Works the other way too.
I have just started using std::variant in my projects. I have a doubt. What will the destructor of std::variant do in the code shown below. Variant holds a void* data. Once variant goes out of scope, I think it will only free the memory of void* but not the actual object the pointer was pointing to. So there will be memory leak in this case. I would like to know if my understanding is correct or not.
#include <iostream>
#include <memory>
#include <variant>
using namespace std;
class A {
public:
~A(){
cout<<"Destructor called"<<endl;
}
};
int main() {
std::variant<void*> data;
A* b = new A();
data = (void*)b;
return 0;
}
When the variant destructor fires, it will call the destructor for whatever type of item is stored in the variant at that point. If that’s a void*, then C++ will say “okay, I will clean up the void*, and since that’s a primitive type, that’s a no-op.” It won’t look at the void*, realize that it’s actually a pointer to an A, and then delete the pointer as though it’s an A*.
The comments have pointed out that it’s fairly unusual to use a variant of a void*. A void* means “I’m pointing at something, and it’s up to you as the user to keep track of what it is and do the appropriate casting and resource management.” A variant means “I’m holding one of the following actual things, and I want C++ to remember which one and to do the appropriate resource management for me.” You may want to rethink your design, as there might be an easier way to do whatever you’re aiming to do here.
You are correct. The only pointer owning classes in the standard library that actually does delete (or delete[]) on pointers are the smart pointers.
std::variant is supposed to support you to hold one object of any number of types and primarily not pointers to objects. If the variant contains pointers, it means that some other object owns the data and is responsible for deleting it.
A std::variant capable of holding only one type is rarely useful either. You can declare the variable as a normal variable of that type in that case.
Here's one example of using a std::variant capable of holding objects of two unrelated types, and destruction will happen as expected.
#include <iostream>
#include <variant>
class A {
public:
~A() { std::cout << "A destructor called\n"; }
};
class B {
public:
B() {}
B(const B&) = default;
B& operator=(const B&) = default;
~B() { std::cout << "B destructor called\n"; }
};
int main() {
std::variant<A, B> data; // now holds a default constructed A
data = B(); // deletes the A and now holds a default constructed B
std::cout << "---\n";
}
Output:
A destructor called // in "data = B()", the A must be destroyed
B destructor called // the temporary B used in "data = B()"
---
B destructor called // the B in the variant when the variant goes out of scope
I have the following code snippet:
#include <iostream>
using namespace std;
class A {
int* data;
int size;
public:
A(int s):size(s)
{
data = new int[size];
}
A() {
data = nullptr;
}
~A() {
if (data) delete [] data;
}
};
class B {
A a[2];
public:
B() {
a[0] = A(10);
a[1] = A(11);
}
};
int main(int argc, char *argv[]) {
B b;
}
In the C++ code above, I have class A which has an array member int* data, and the (de)allocation of memory are handled by (de)constructor. The I created class B which has an array of class A of fixed length as a data member.
My question is: how to elegantly initialise the member A a[2]? In the code above, the A(10) and A(11) are created on the stack, when jumping out of the scope, their destructors will be called, hence the data comes invalid. When jumping of the main function's scope, the pointers held by a[2] will be deallocated twice, causing the error:
pointer being freed was not allocated.
One possible solution is to carefully design a copy constructor and a move constructor, by doing so the above coding paradigm could work.
Another solution I've tried is to initialise the array in the initialization list of class B:
B() : a { A(10), A(11) }
This solution works and I don't really tell the underlying mechanism of initialization list. I think it must be quite different from simply construct and copy. I really expected some experts could give an elaborate explanation of this mechanism. Of course, this solution is ugly hard-coded and not flexible.
So I wonder if there are some programming paradigms in C++ to tackle this design problem?
In the code above, the A(10) and A(11) are created on the stack
They are temporary objects. It is not specified where they are created or if they're created at all.
when jumping out of the scope, their destructors will be called
The destructor of each temporary will be called after the corresponding move assignment statement ends.
One possible solution is to carefully design a copy constructor and a move constructor, by doing so the above coding paradigm could work.
And {copy,move} assignment operator too. You should always do that when the implicitly declared ones don't do the right thing. And they never do the right thing if you delete something in the destructor.
Another solution I've tried is to initialise the array in the initialization list of class B
This solution works and I don't really tell the underlying mechanism of initialization list. I think it must be quite different from simply construct and copy.
The bug in the original code is badly behaving move assignment operator of A. Since the initialization list never move assigns from a temporary, it never triggers the bug.
This is actually the more elegant way to construct a that you asked for. Not because it avoids the bug, but because avoiding unnecessary moving is good thing, intrinsically.
So I wonder if there are some programming paradigms in C++ to tackle this design problem?
Yes. RAII and Single responsibility principle. Unless your class does nothing else, besides managing the memory pointed by data, it should not be managing the memory. Instead, it should delegate the memory management to a RAII object. In this case, you should use a std::vector member.
class A {
std::vector<int> data;
public:
A(int s):data(s) {}
A() = default;
};
Using an initializer list to construct B::a, like this:
class B {
A a[2];
public:
B() : a({10, 11}){
}
};
The ideal answer would be to force A to use movements instead of copies, or on a copy to allocate new space for the item. Of the two, the most efficient is the former and so I will expand on it below:
Forcing movement can be done in two fashions:
Delete the copy constructor and copy operator=, and implement your own move constructor and operator=
Consistently use std::move and std::swap.
Of these, the former is superior in that you will be unable to accidentally copy the class, but with the latter the fact that you are moving will be more evident.
To delete the default copy methods do:
class A {
A( const A& a ) = delete;
A& operator =( const A& a ) = delete;
}
I'm fairly new to Pass By Reference, and I HAVE to make sure I understand this correctly. I have to convert all my Heap memory to Stack memory because my professor said so, and I'm stuck on two concepts.
What is the best way to store a reference in a class? I originally had member objects as non pointers, but noticed the deconstructor would be called on the member variable when the object (not member object) was popped off the stack. This makes me think it was a copy, and not actually a reference.
Here is an example of what I had originally:
class B
{
public:
B();
~B();
};
class A
{
private:
B b;
public:
A();
~A();
setB(B& bVar);
};
void A::setB(B& bVar)
{
b = bVar;
}
My solution was to change it to a pointer so it didn't call the deconstructor, but I'M NOT SURE IF THIS IS THE CORRECT WAY TO DO IT. Here was my solution:
class B
{
public:
B();
~B();
};
class A
{
private:
B* b;
public:
A();
~A();
setB(B& bVar);
};
void A::setB(B& bVar)
{
b = &bVar;
}
My second question is kind of related. I'm not sure what exactly happens when you have:
object1 = object2&.
Is object1 a copy or is it actually another identifier for object2?
References behave like symbolic aliases to instances, and are in some respects like "pointers" that can't (shouldn't) be null. For the sake of this explanation, I'll refer to them below as though they were pointers.
When you have a T&, it means that it is pointing to a T, and is not itself a copy.
When you have a T = T&, it means you'll get a copy (or a copy of a copy) depending on how the constructor or assignment operator are defined.
When you have an R& = L, it means you'll get copy of L into whatever the R& is pointing to (provided the assignment operator of R permits this).
Concerning the "correct" way of storing references, I would ask at least these questions:
Is it acceptable for the member reference to remain the same throughout the containing object's lifetime?
Will instances of the containing type always be destroyed before the object(s) pointed to by the member reference?
If both are true, then simply declaring and appropriately initializing a member T& should suffice:
class B
{
// details...
};
class A
{
B &_b;
public:
A(B &b) :
_b(b)
{}
};
Otherwise, despite the requirements imposed upon you, the situation might call for something like shared_ptr<> or similar.
References to objects living on the stack, in-turn held by other objects that themselves may be constructed in such a way that they will outlive their reference's lifespan are merely pointers waiting to dangle.
Consider copying, or arguing that heap-allocated memory is the better option.
If you are uncertain of the reference network induced by your program, you need to redesign it.
EDIT:
It is important to note that, when passing a reference to a function (const T& in particular), there are certain situations in which it can be elided by the compiler. For example: When such a function is inlined, references can be replaced by more efficient addressing logic than if they were required to be pointers.
In this respect, they are not pointers.
Suppose I have a class
class A {
public:
A(int i);
~A();
private:
B b; // Want <- this guy to be constructed with i in A's constructor!
};
I want b to be constructed in the constructor with particular parameters that aren't known until A is constructed. If I were to do the following in A's constructor:
A::A(int i) {
B b(i);
// Or even if I try to do b = B::B(i);
}
I notice that b get's allocated twice on the stack! aghghg.
Then I found out that what I can do in A's constructor is:
A::A() : b(B::B(7)) {
}
And b only gets allocated on the stack once!
But this is pretty clunky. Anyone got a better idea? Remember, the constructor should only be called once!
Is this the standard way of allocating objects NON-dynamically with important parameters? What if we can shove b's construction into that fancy argument list thing!? You're forced to either dynamically allocate, or construct TWICE on the stack!
Bonus Question: When does b get deallocated? Is it after or right before A's destructor
I'm sorry to say but you have it all wrong.
What you need to do is to pick up a good beginner's C++ book. This is such a fundamental part of the language that if you don't understand this you will struggle when dealing with non-trivial C++ code.
That being said, when an object is about to be created, all subobjects will be created first. If you need to pass parameters to those subobject constructors, you need to create what's called an initializer list:
A::A(int i) : b(i) {}
The stuff that follows the colon and before the first brace is the initializer list. Only constructors can have them. What's going on here is that we pass the value of i to the b subobject's constructor. This happens before the constructor for A is called!
So for your case, the order of construction is:
The b subobject
The A object itself
The order of destruction is the complete opposite process.
does
A::A() : b(7) { }
not work?
Edit: I'm at work, so I'll do a more comprehensive edit later using some profile stuff to see what gcc does w.r.t. deallocation. I suspect that nobar is right and all deallocation happens at once.
b(B::B(7)) works as well as b(7) because B::B(7) creates a temporary B variable. b is then copy-constructed from that temporary. A decent optimizing compiler should be able to reduce the second case to the first but:
b(7) is more idiomatic -- other c++ programmers will recognize is more easily
You really don't know for sure what a compiler will do.
if B is not copy-constructible, or expensive to copy-construct, you may not want to deal with the added overhead if, like most, you turn off optimizations for debugging.
It only takes a small modification to your program to demonstrate the order of construction and destruction.
#include <iostream>
using std::cerr;
class B
{
public:
B( int ) { cerr<<"B()\n"; ;}
~B() { cerr<<"~B()\n"; }
};
class A
{
B b;
public:
A( int i ) : b(i) { cerr<<"A()\n"; }
~A() { cerr<<"~A()\n"; }
};
int main()
{
A a(7);
}
Here's the output:
$ make destructor_order && ./destructor_order
B()
A()
~A()
~B()