Questions about vector, union, and pointers in C++ - c++

The questions I have are NOT homework questions, but I am considering using these concepts in my assignment. The context, if it helps, is like this: I need to keep track of several union instances and they belong to my own union in one of my own classes as class variables. (Note: the number of union instances is unknown, so I cannot just have a fixed number of union instances.
Q1: If I have a union, say MyUnion,
and many instances of this union,
can I then put them into a vector
like
vector<union MyUnion> myVector(10);
Q2: Is it valid to have a pointer of
union? Like
union MyUnion *myUnionPtr = new union myUnion;
Q3: I am considering using a vector
of union pointers in my
implementation, is this concept
correct? Also, is it a normal
approach in C++? Do I need to
rethink about my design?

If the union is CopyConstructable and Assignable, then it meets the requirements for std::vector.
Yes: MyUnion* ptr = new MyUnion();
A container of pointers works in some situations, but if you want a container of owning pointers, look at Boost's ptr_* containers. However, here it appears you'd either have a container of non-pointers or a container of non-owning pointers, either of which is fine and common for vector.
All types are, by default, both CopyConstructable and Assignable. ("Copyable" is used to mean the union of these concepts, however the standard specifies them individually.) This is because copy ctors and op= are added to classes (unions are one class-type) except under certain circumstances. There are a few references online, but I'm not aware of a single one, available freely online, that spells these out.
You have to go out of your way to prevent it, such as:
make the copy ctor or op= non-public
make the copy ctor or op= take a non-const reference
give the class type a non-CopyConstructable or non-Assignable member
Example:
union CopyableUnion {
int n;
char c;
double d;
};
union NonCopyableUnion {
int n;
char c;
double d;
NonCopyableUnion() {} // required, because any user-defined ctor,
// such as the private copy ctor below, prevents the supplied
// default ctor
private:
NonCopyableUnion(NonCopyableUnion const&);
NonCopyableUnion& operator=(NonCopyableUnion const&);
};
int main() {
CopyableUnion a;
CopyableUnion b = a; // fine, uses copy ctor
b = a; // fine, uses op=
NonCopyableUnion c;
NonCopyableUnion d = c; // compile error (copy ctor)
d = c; // compile error (op=)
return 0;
}
Note: And just because something is Copyable, doesn't mean it does what you want! Example:
struct A {
int* p;
A() : p(new int()) {}
// the provided copy ctor does this:
//A(A const& other) : p(other.p) {}
// which is known as "member-wise" copying
~A() { delete p; }
};
int main() {
A a;
{
A b = a;
assert(b.p == a.p); // this is a problem!
} // because when 'b' is destroyed, it deletes the same pointer
// as 'a' holds
return 0; // and now you have Undefined Behavior when
// ~A tries to delete it again
}
The same thing holds true for unions, of course. The fix applies equally, though, as well:
struct A {
int* p;
A() : p(new int()) {}
A(A const& other) : p(new int(*other.p)) {}
~A() { delete p; }
};
(If you spotted it, yes, A has a problem if you ever try to use op=, in just the same way as it originally had with the copy ctor.)

Related

move class instance holding a unique_ptr

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

What will destructor of a std::variant do if it contains void* data

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

Should the assignment operator allocate new memory for data members or reuse existing memory?

I have a question regarding the assignment operator (apologies if this has already been answered in a different post).
As I understand the assignment operator, it is suppose to assign the value of one object to another, e.g.
class A {
public:
A();
A& operator=(const A& rhs)
{
b = rhs.b;
return *this;
}
private:
B b;
};
Now, if the class contains pointers, it seems to be common practice to allocate new memory in the assignment operator. This makes the assignment operator more complicated since one needs to be more careful, e.g.
class A {
public:
A() : b(0)
{
b = new B;
}
A& operator=(const A& rhs)
{
if (this != &rhs) {
B* b1 = 0;
try {
b1 = new B(*rhs.b);
}
catch {
delete b1;
throw;
}
delete b;
b = b1;
}
return *this;
}
private:
B* b;
};
So my question is: Why not assign the value of the existing object pointed to, i.e. reuse the memory by calling B's assignment operator as one would do if b was not a pointer (see my first example)? Then the assignment would look like this
class A {
public:
A() : b(0)
{
b = new B;
}
A& operator=(const A& rhs)
{
*b = *rhs.b;
return *this;
}
private:
B* b;
};
This is much more simple and does exactly what I would expect from the assignment operator.
Moreover, I can think of at least one example where the more complicated assignment operator will get me into trouble: If class A has a member function which returns b
const B* A::GetB() const
{
return b;
}
then the following code will leave a dangling pointer
A a1, a2;
const B* my_b = a1.GetB();
a1 = a2; // this leaves my_b dangling!
Your comments are much appreciated. Thanks!
You should always reuse existing memory whenever possible. In you case, you should reuse the existing memory only if you are dead certain that b points to valid memory.
It depends on the exact scenario at hand really. Usually, it is a good practice to reuse the memory, but really, when you are trying to assign a differently sized internal array, etc, you might be better off with reallocating or delete/new.
Note that, the own-cope check at the beginning would still be desired to avoid A = A alike situation, however.
I can't think of any reason that you shouldn't try to do that. The examples that I have found where the memory is deleted is due to the fact that the type itself wasn't copyable. If you have a class attribute and it is a pointer to a copyable type then your suggestion should work conceptually (meaning that you only posted a partial snip so I am not saying that what is in your example is perfect). Some older examples show how it is done with char* and int* or other types of dynamic arrays. However, modern C++ programmers should be trying to use std::string and container classes. Now we also have smart pointers so that you can have shared pointers without having to worry about dangling references or worrying about which object is really the owner of it. Below is an example of what I mean. The example does use a template but copies as though the T is a c-array (not a container class) so deep copying has to be done as in your first example.
http://www.cplusplus.com/articles/y8hv0pDG/

When is aggregate initialisation valid in C++11?

Lets say we have the following code:
#include <iostream>
#include <string>
struct A
{
A() {}
A(const A&) { std::cout << "Copy" << std::endl; }
A(A&&) { std::cout << "Move" << std::endl; }
std::string s;
};
struct B
{
A a;
};
int main()
{
B{A()};
}
Here, I believe struct A is not an aggregate, as it has both non-trivial constructors and also a std::string member which I presume is not an aggregate. This presumably means that B is also not an aggregate.
Yet I can aggregate initialize B. In addition this can be done without either the copy nor move constructor being called (e.g. C++0x GCC 4.5.1 on ideone).
This behavior seems like a useful optimization, particularly for composing together large stack types that don't have cheap moves.
My question is: When is this sort of aggregate initialization valid under C++0x?
Edit + follow up question:
DeadMG below answered with the following:
That's not aggregate initialization at all, it's uniform initialization, which basically in this case means calling the constructor, and the no copy or move is probably done by RVO and NRVO.
Note that when I change B to the following:
struct B
{
A a;
B(const A& a_) : a(a_) {}
B(A&& a_) : a(std::move(a_)) {}
};
A move is performed.
So if this is just uniform initialization and just calling the constructor and doing nothing special, then how do I write a constructor that allows the move to be elided?
Or is GCC just not eliding the move here when it is valid to do so, and if so, is there a compiler and optimization setting that will elide the move?
According to the new standard, clause 8.5.1 (Aggretates), a sufficiently simple type (e.g. no user-defined constructors) qualifies as an aggregate. For such an aggregate Foo, writing Foo x{a, b, ... }; will construct the members from the list items.
Simple example:
struct A
{
std::unordered_map<int, int> a;
std::string b;
std::array<int,4> c;
MyClass d; // Only constructor is MyClass(int, int)
};
// Usage:
A x{{{1,-1}, {12, -2}}, "meow", {1,2,3,4}, MyClass(4,4)};
// Alternative:
A x{{{1,-1}, {12, -2}}, "meow", {1,2,3,4}, {4,4}};
The object x is constructed with all the relevant constructors executed in place. No maps or strings or MyClasses ever get copied or moved around. Note that both variants at at the bottom do the same thing. You can even make MyClass's copy and move constructors private if you like.
That's not aggregate initialization at all, it's uniform initialization, which basically in this case means calling the constructor, and the no copy or move is probably done by RVO and NRVO.

std::vector of objects and const-correctness

Consider the following:
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
vector<A> vec;
vec.push_back(foo); // <-- compile error!
return 0;
}
Obviously, the copy constructor is not enough. What am I missing?
EDIT:
Ofc. I cannot change this->c in operator=() method, so I don't see how operator=() would be used (although required by std::vector).
I'm not sure why nobody said it, but the correct answer is to drop the const, or store A*'s in the vector (using the appropriate smart pointer).
You can give your class terrible semantics by having "copy" invoke UB or doing nothing (and therefore not being a copy), but why all this trouble dancing around UB and bad code? What do you get by making that const? (Hint: Nothing.) Your problem is conceptual: If a class has a const member, the class is const. Objects that are const, fundamentally, cannot be assigned.
Just make it a non-const private, and expose its value immutably. To users, this is equivalent, const-wise. It allows the implicitly generated functions to work just fine.
An STL container element must be copy-constructible and assignable1(which your class A isn't). You need to overload operator =.
1
: §23.1 says The type of objects stored in these components must meet the requirements of CopyConstructible
types (20.1.3), and the additional requirements of Assignabletypes
EDIT :
Disclaimer: I am not sure whether the following piece of code is 100% safe. If it invokes UB or something please let me know.
A& operator=(const A& assign)
{
*const_cast<int*> (&c)= assign.c;
return *this;
}
EDIT 2
I think the above code snippet invokes Undefined Behaviour because trying to cast away the const-ness of a const qualified variable invokes UB.
You're missing an assignment operator (or copy assignment operator), one of the big three.
The stored type must meet the CopyConstructible and Assignable requirements, which means that operator= is needed too.
Probably the assignment operator. The compiler normally generates a default one for you, but that feature is disabled since your class has non-trivial copy semantics.
I think the STL implementation of vector functions you are using require an assignment operator (refer Prasoon's quote from the Standard). However as per the quote below, since the assignment operator in your code is implicitly defined (since it is not defined explicitly), your program is ill-formed due to the fact that your class also has a const non static data member.
C++03
$12.8/12 - "An implicitly-declared
copy assignment operator is implicitly
defined when an object of its class
type is assigned a value of its class
type or a value of a class type
derived from its class type. A program
is illformed if the class for which a
copy assignment operator is implicitly
defined has:
— a nonstatic data member of const type, or
— a nonstatic data
member of reference type, or
— a
nonstatic data member of class type
(or array thereof) with an
inaccessible copy assignment operator,
or
— a base class with an inaccessible
copy assignment operator.
Workaround without const_cast.
A& operator=(const A& right)
{
if (this == &right) return *this;
this->~A();
new (this) A(right);
return *this;
}
I recently ran into the same situation and I used a std::set instead, because its mechanism for adding an element (insert) does not require the = operator (uses the < operator), unlike vector's mechanism (push_back).
If performance is a problem you may try unordered_set or something else similar.
You also need to implement a copy constructor, which will look like this:
class A {
public:
const int c; // must not be modified!
A(int _c)
...
A(const A& copy)
...
A& operator=(const A& rhs)
{
int * p_writable_c = const_cast<int *>(&c);
*p_writable_c = rhs.c;
return *this;
}
};
The special const_cast template takes a pointer type and casts it back to a writeable form, for occasions such as this.
It should be noted that const_cast is not always safe to use, see here.
I just want to point out that as of C++11 and later, the original code in the question compiles just fine! No errors at all. However, vec.emplace_back() would be a better call, as it uses "placement new" internally and is therefore more efficient, copy-constructing the object right into the memory at the end of the vector rather than having an additional, intermediate copy.
cppreference states (emphasis added):
std::vector<T,Allocator>::emplace_back
Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container.
Here's a quick demo showing that both vec.push_back() and vec.emplace_back() work just fine now.
Run it here: https://onlinegdb.com/BkFkja6ED.
#include <cstdio>
#include <vector>
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
// Copy constructor
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
A foo2(999);
std::vector<A> vec;
vec.push_back(foo); // works!
vec.emplace_back(foo2); // also works!
for (size_t i = 0; i < vec.size(); i++)
{
printf("vec[%lu].c = %i\n", i, vec[i].c);
}
return 0;
}
Output:
vec[0].c = 1337
vec[1].c = 999