In C++, I have a class MyClass, which during construction takes a reference to an int to create an internal reference to it.
I then have a class BigClass, containing both an std::vector<int> vecInt_ and an std::vector<MyClass> vecMyClass_. The constructor of BigClass takes as an argument the size of the vectors vecInt_ and vecMyClass_. In the constructor of BigClass, I would like to have each element of vecMyClass_ use in its constructor the corresponding element of vecInt_.
How could I write that ? If I could call the constructors of vecMyClass from the body of the constructor of BigClass, that would look like that :
BigClass(int nbElem) :
vecInt_(nbElem),
vecMyClass_(nbElem)
{
for (int i = 0; i < nbElem; ++i)
{
vecMyClass_[i](vecMyInt_[i]);
}
}
But of course the parenthesis here would mean operator(), and not the constructor. I cannot write something like:
vecMyClass_[i] = MyClass(vecMyInt_[i]);
Because MyClass contains a reference and not a pointer, and thus referenced value can not be modified.
You could initialize vecMyClass_ as an empty vector and emplace_back elements into it while you construct them:
BigClass(int nbElem) :
vecInt_(nbElem),
vecMyClass_() //empty vector
{
vecMyClass_.reserve(nbElem); //avoid reallocations
for (int i = 0; i < nbElem; ++i)
{
vecMyClass_.emplace_back(vecInt_[i]);
}
}
It doesn't sound like a very good idea. At some point adding elements to vecMyInt_ will result in expanding the vector, i.e. allocating new memory and moving the elements there, and freeing the old memory. This means that the references kept by instances of MyClass will be invalid.
Of course this won't be a problem if you reserve the capacity beforehand, and never add elements to the vector.
#include <vector>
#include <iostream>
struct MyClass {
int& x;
MyClass(int& x) : x(x) {}
};
struct BigClass {
BigClass(std::size_t nb_elems) : ints(nb_elems) {
my_classes.reserve(nb_elems);
for(int& x : ints) {
my_classes.emplace_back(x);
}
}
std::vector<int> ints;
std::vector<MyClass> my_classes;
};
int main()
{
BigClass b{10};
for(int& x : b.ints) {
x = 23;
}
// they are all 23
for(auto& c : b.my_classes) {
std::cout << c.x << std::endl;
// change them
c.x = 24;
}
// they are all 24 now
for(auto& c : b.ints) {
std::cout << c << std::endl;
}
return 0;
}
Related
I'm trying to create a vector of pointers pointing to objects of a specific class.
This is my code and I expected each pointer to point to different objects. But turns out they have the same address values. Why?
class A{
public:
int a;
A(): a(0){}
A(int a): a(a){}
};
int main() {
vector<A*> v(2, new A());
cout<<v[0]<<endl; // will print 0x602000000010
cout<<v[1]<<endl; // will print 0x602000000010 (same)
return 0;
}
As pointed out in the comments, the constructor for std::vector that you use will fill the vector with count (the 1st argument) copies of the value of the 2nd argument. In your case, those will each be copies of the address of the single A object created by the call to new.
To fill a vector with the addresses of multiple objects, you can use the std::generate function, passing a lambda, which creates a new object and returns its address, as the 'generator function' (3rd argument):
#include <vector>
#include <iostream>
#include <algorithm> // For std::generate
class A {
public:
int a;
A() : a(0) {}
A(int a) : a(a) {}
};
int main()
{
std::vector<A*> v(2);
std::generate(v.begin(), v.end(), [] { return new A; });
std::cout << v[0] << std::endl;
std::cout << v[1] << std::endl;
for (auto p : v) delete p; // The vector's destructor WON'T delete pointers
// Or, to use the other constructor for A...
std::vector<A*> vx(4);
int i = 0;
std::generate(vx.begin(), vx.end(), [&i] { return new A(i++); });
for (auto e : vx) std::cout << e->a << std::endl; // 0, 1, 2, 3...
for (auto p : vx) delete p;
return 0;
}
Note that the destructor for a std::vector will properly delete object elements (calling the destructor for each) but it won't delete objects pointed-to by its elements; you have to do that yourself.
If I have a class with members like this:
class MyClass {
public:
void set_my_vector() {
for (int ind = 0; ind < 3; ++ind) {
my_vector.push_back(new MyStruct(i, i*2));
}
}
private:
struct MyStruct {
int num_a;
int num_b;
MyStruct(int i, int j) : num_a(i), num_b(j) {}
};
std::vector<MyStruct*> my_vector;
};
Do I need to write the rule-of-five functions, or will std::vector take care of deep copying and deleting the elements allocated on the heap?
EDIT:
The following code uses default copy constructor, so I assume that after I copy my_class1 object into my_class2 object, the elements of my_class1.my_vector and my_class2.my_vector will be the same, because the MyStruct pointers were copied, but not the data itself. However, the output shows that they are not the same. You can run the code here: https://onlinegdb.com/S1pK9YE4v
#include <iostream>
#include <vector>
class MyClass {
public:
void fill_my_vector(int i, int j) {
my_vector.clear();
for (int ind = 0; ind < 3; ++ind) {
my_vector.push_back(new MyStruct(i, j));
}
}
void print () {
for (int ind = 0; ind < 3; ++ind) {
std::cout << my_vector[ind]->int1 << ", " << my_vector[ind]->int2 << std::endl;
}
std::cout << std::endl;
}
private:
struct MyStruct {
MyStruct (int i, int j) :
int1(i), int2(j)
{}
int int1;
int int2;
};
std::vector<MyStruct*> my_vector;
};
int main()
{
MyClass my_class1;
my_class1.fill_my_vector(42, 43);
std::cout << "my_class1: " << std::endl;
my_class1.print();
MyClass my_class2 = my_class1;
my_class2.fill_my_vector(12, 13);
std::cout << "my_class2: " << std::endl;
my_class2.print();
std::cout << "my_class1: " << std::endl;
my_class1.print();
}
EDIT2: I know about smart pointers. I am specifically interested what happens if I use raw pointers.
You need to implement the copy constructor, copy assignment and destructor.
Additionally, consider changing your vector declaration from
std::vector<MyStruct*> my_vector;
to
std::vector<std::unique_ptr<MyStruct>> my_vector;
so that it actually owns the heap allocated objects properly. Doing this change will help you not write a destructor.
No, std::vector doesn't take care of deep copying of your objects stored by pointer. You have few possibilities to solve this:
Store MyStruct by value.
Store std::unique_ptr<MyStruct>.
Store std::shared_ptr<MyStruct>.
Note that because MyStruct contains only fields of the primitive types, neither of copy constructor, assignment operator and destructor are needed, otherwise you'd have to implement them, default implementation which compiler will generate automatically will be good enough.
I needed a vector of a base class, and everywhere I looked the solution was to store it as a pointer. However, storing pointers makes things harder to use because of the lack of value semantics.
struct base
{
int a = 10;
};
struct derived : public base
{
int b = 5;
};
int main()
{
std::vector<std::reference_wrapper<base>> vec;
vec.push_back(*(new derived));
auto elem = static_cast<derived*>(&vec.at(0).get());
std::cout << elem->b << std::endl; // prints 5
return 0;
}
Is there any downside to just storing them as an std::reference_wrapper?
I have some code that claims ownership of a sequence of raw pointers, and am wondering if there is an acceptable way to do this? What I'm looking for is a way to enforce the ownership in code to a greater degree. Mainly, I've been wondering whether or not my constructor should be taking a vector of unique pointers directly.
As a sidenote, once ownership has been claimed, the data is supposed to be immutable.
The code follows roughly the pattern of class X below.
#include <iostream>
#include <memory>
#include <vector>
using namespace std; // For readability purposes only
class X {
public:
const vector< unique_ptr<const int> > data; // In my case this is private
// Constructor: X object will take ownership of the data
// destroying it when going out of scope
X (vector<int*> rawData)
: data { make_move_iterator(rawData.begin()), make_move_iterator(rawData.end()) }
{ }
};
int main() {
// Illustrating some issues with claiming ownership of existing pointers:
vector<int*> rawData { new int(9) , new int(4) };
int* rawPointer = rawData[0];
{ // New scope
X x(rawData);
cout << *(x.data[0]) << endl; // Unique pointer points to 9
*rawPointer = 7;
cout << *(x.data[0]) << endl; // Unique pointer points to 7
}
cout << *rawPointer << endl; // The pointer has been deleted, prints garbage
return 0;
}
It is difficult to post an answer without detailed knowledge of your situation. But my recommendation is to attach your data to a unique_ptr as soon as it is known. Then you can move that unique_ptr into and out of vectors at will. For example:
#include <iostream>
#include <memory>
#include <vector>
using namespace std; // For readability purposes only
class X {
public:
const vector< unique_ptr<const int> > data; // In my case this is private
// Constructor: X object will take ownership of the data
// destroying it when going out of scope
X (vector<unique_ptr<const int>>&& v)
: data { std::move(v) }
{ }
};
vector<unique_ptr<const int>>
collectRawData()
{
auto rawData = {9, 4};
vector<unique_ptr<const int>> data;
for (auto const& x : rawData)
data.push_back(make_unique<int>(x));
return data;
}
int main() {
auto rawData = collectRawData();
{ // New scope
X x(std::move(rawData));
cout << *(x.data[0]) << endl; // Unique pointer points to 9
cout << *(x.data[1]) << endl; // Unique pointer points to 4
}
}
You did several misstakes.
In case of const vector< unique_ptr<const int> > data; a move iterator does make not that much sense. The reason why is, int* doesn't have a move constructor.
If you call X's constructor X (vector<int*> rawData) with vector < int* > so the copy constructor of vector < int* > gets called, but that's not what you want to.
Btw. the reason why to use move is, to avoid big memory copies. For instance std::vector < int* >: The member attribute size and the pointer to the memory location where your int*s are stored of std::vector<int*> must be copied by a move too but not the int*s self. A conclusion is that move is there to claim ownership.
If you want shared pointers like that, use std::shared_ptr. It owns a counter, which counts the ptrs which pointing to itself.
ยด
My Example Code:
class X
{
public:
const std::vector< std::shared_ptr< const int> > data; // In my case this is private
// Constructor: X object will take ownership of the data
// destroying it when going out of scope
X (std::vector<std::shared_ptr<int>>& rawData)
//: data(rawData)
: data(rawData.cbegin(), rawData.cend())
{ }
};
int main() {
// Illustrating some issues with claiming ownership of existing pointers:
std::vector<std::shared_ptr<int>> rawData { std::make_shared<int>(9), std::make_shared<int>(4) };
int* rawPointer = rawData[0].get();
{ // New scope
X x(rawData);
cout << *(x.data[0]) << endl; // Unique pointer points to 9
*rawPointer = 7;
cout << *(x.data[0]) << endl; // Unique pointer points to 7
}
cout << *rawPointer << endl; // The pointer has been deleted, prints not more garbage
return 0;
}
If you dont want use std::shared_ptr, you will need an GC.
I have objects of different types derived from a single super-type. I wonder if there are any disadvantages in using std::initializer list in a range for loop like this:
for(auto object: std::initializer_list<Object *>{object1, object2, object3}) {
}
Is it completely OK and efficient or would it be better to use an array? To me the std::array solution seems to be more restrictive for the compiler and there is a disadvantage of explicitly stating the size:
for(auto object: std::array<Object*, 3>{object1, object2, object3}) {
}
Is there any other or nicer way of iterating over an explicitly given list of objects?
There is no need to use the verbose std::initializer_list inside the loop
#include <iostream>
#include <initializer_list>
struct B { virtual int fun() { return 0; } };
struct D1 : B { int fun() { return 1; } };
struct D2 : B { int fun() { return 2; } };
int main()
{
D1 x;
D2 y;
B* px = &x;
B* py = &y;
for (auto& e : { px, py })
std::cout << e->fun() << "\n";
}
Live Example.
If you want to do it on-the-fly without defining px and py, you can indeed use std::initializer_list<B*>{ &x, &y } inside the loop.
You can simply write
for(auto object : {object1, object2, object3}) {
// work
}