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.
Related
I am trying to convince myself that objects in C++ have constant address during their lifetime. Here is a minimal working example:
#include <iostream>
#include <type_traits>
#include <vector>
class Class1
{
public:
Class1(unsigned int * pt);
unsigned int * val_pt;
};
Class1::Class1(unsigned int * pt)
:
val_pt(pt)
{}
class Class2
{
public:
Class2(std::vector<unsigned int> vec_);
std::vector<unsigned int> vec_of_ints;
Class1 class1_instance;
};
Class2::Class2(std::vector<unsigned int> vec_)
:
vec_of_ints(vec_),
class1_instance(Class1(&vec_of_ints[0]))
{}
int main() {
std::vector<unsigned int> vec_test(10, 2);
Class2 instance_class2(vec_test);
Class1 instance_class1 = instance_class2.class1_instance;
//both addresses are equal
std::cout<<"Address stored in instance_class1: "<<instance_class1.val_pt<<" ,address of first vec_element of instance_class2: "<<&(instance_class2.vec_of_ints)[0]<<std::endl;
instance_class2.vec_of_ints.resize(20);
//different addresses now
std::cout<<"Address stored in instance_class1: "<<instance_class1.val_pt<<" ,address of first vec_element of instance_class2: "<<&(instance_class2.vec_of_ints)[0]<<std::endl;
return 0;
}
My Class2 stores a vector of ints and an instance of Class1. Class1 stores the address of the vector of the Class2 instance.
I'd like to get the address of that vector, i.e. the address where the vector is stored on the stack. If my understanding is correct, the resize() function doesn't change that address on the stack but only the content of that address, i.e. where the vector points to in heap.
My overall goal is to show that any modifications of the vector in Class2 are "visible" in the stored pointer of Class1. So if I dereference the pointer in Class1 I will get the same integer value as when accessing the vector itself in Class2. That is because the address of member variables are constant during runtime.
But I guess something is wrong in my code, probably in the constructor where I pass 'vec[0]'. I think this is not the actual address of the vector in the stack but some address on the heap. How do I get the correct address?
Any input is appreciated!
What you're doing here is ungodly, and needs to stop. Consider this particularly terrible pattern:
class DataOwner {
public:
inline std::vector<uint32_t>& getData() { return data; }
private:
// I am safely tucked away
std::vector<uint32_t> data;
};
class I_Want_To_Work_On_Data {
public:
I_Want_To_Work_On_Data(DataOwner* owner) : owner(owner) {}
void doThing() {
auto& direct_ref_to_data = owner->getData();
for(auto& item : direct_ref_to_data) {
// This is just as fast as your direct pointer :/
}
}
private:
DataOwner* owner;
};
Returning mutable access to the data is somewhat bad, but it's far safer than the approach you are taking (in a single threaded environment a least). Performance is no worse than what you are attempting, but it is a lot safer. So what are you optimising this for exactly? How is your approach an improvement over this boring pattern?
Now you could argue that providing mutable access to the std::vector isn't wanted (i.e. don't allow any old code to resize the array), but that can easily be solved without resorting to dirty hacks.
#include <vector>
#include <cstdint>
class DataOwner {
public:
inline std::vector<uint32_t>::iterator begin()
{ return data.begin(); }
inline std::vector<uint32_t>::iterator end()
{ return data.end(); }
inline std::vector<uint32_t>::const_iterator begin() const
{ return data.begin(); }
inline std::vector<uint32_t>::const_iterator end() const
{ return data.end(); }
private:
// I am safely tucked away
std::vector<uint32_t> data;
};
class ConstAccess {
public:
ConstAccess(const DataOwner& owner) : owner(owner) {}
void doThing() {
for(const auto& item : owner) {
}
}
private:
const DataOwner& owner;
};
class MutableAccess {
public:
MutableAccess(DataOwner& owner) : owner(owner) {}
void doThing() {
for(auto& item : owner) {
}
}
private:
DataOwner& owner;
};
The performance is the same as with your approach, however this approach as the following advantages:
It won't crash in debug builds on this line: class1_instance(Class1(&vec_of_ints[0])), when the vector is empty, and you attempt to dereference NULL to find the address.
It won't crash when you attempt to dereference unsigned int * val_pt; after you've accidentally resized the array.
It won't allow you to accidentally do: delete [] val_pt
I'm not sure what conclusions you extracted from the comments and responses above.
I just wanted to make sure these were not among them:
The address of a member variable is constant during runtime.
If, for example, you have a vector of Class2 instances, and you
resize that vector, the address of the vec_of_ints member variable
may change for any of those instances.
Having a Class2 instance in the stack or a pointer to a Class2 instance in the heap makes a difference.
The address of the vec_of_ints member variable shouldn't change if
you resize it, no matter the instance of Class2 is in the stack or
in the heap.
The example below tests both assertions (https://godbolt.org/z/3TYrnjro8):
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
struct HoldsIntVector
{
std::vector<int> v{};
};
int main()
{
HoldsIntVector stackInstance{};
auto heapInstance{std::make_unique<HoldsIntVector>()};
stackInstance.v.push_back(5);
heapInstance->v.push_back(5);
auto printStackAndHeapInstances = [&](const auto& text){
std::cout << std::showbase << std::hex;
std::cout << &stackInstance << "\t" << &heapInstance << "\t";
std::cout << &stackInstance.v << "\t" << &heapInstance->v << "\t";
std::cout << std::setw(8) << std::setfill('0') << stackInstance.v.data() << "\t\t";
std::cout << std::setw(8) << std::setfill('0') << heapInstance->v.data() << "\t\t";
std::cout << text;
std::cout << "\n";
};
std::cout << "stackInstance\theapInstance\tstackInstance.v\theapInstance.v\t&stackInstance.v[0]\t&heapInstance.v[0]\n";
printStackAndHeapInstances("after initializing stack and heap instances");
// After resizing both vectors in stack and heap instances
//
// Address of v doesn't change neither in stack nor in heap instances
// Address of v[0] changes in both stack and heap instances
stackInstance.v.resize(10);
heapInstance->v.resize(10);
printStackAndHeapInstances("after resizing both v's");
std::cout << "\n";
// Now what happens if we have a vector of HoldsIntVector and we resize it
//
// Address of v changes for the first HoldsInVector
std::vector<HoldsIntVector> hivs{10};
std::for_each(begin(hivs), end(hivs), [](auto& hiv){hiv.v.push_back(3);});
std::cout << "&hivs[0].v\n" << &hivs[0].v << "\t" << "after intializing hivs\n";
hivs.resize(20);
std::cout << &hivs[0].v << "\t" << "after resizing hivs\n";
}
I can achieve the functionality I need using option 3, however I would like to investigate whether it is possible to create the array on the stack instead.
#include <array>
#include <vector>
struct NotDefaultConstructable
{
NotDefaultConstructable(int val){};
};
int main()
{
//std::array<NotDefaultConstructable, 5> aA; // Fails to compile. [On stack]
//std::vector<NotDefaultConstructable> aV(5); // Fails to compile. [On heap]
std::vector<NotDefaultConstructable> aV; // Compiles. [ On heap]
aV.reserve(5);
}
std::array is an aggregate class type which may have a trivial, implicitly defaulted constructor. If T is not default-constructible, the implicit default constructor is defined as deleted, as per [class.ctor]/5.3.
As this applies in your case, you cannot construct an object of std::array<NotDefaultConstructable, 5> by default construction. You can, however, construct it by means of aggregate initialization:
#include <array>
struct NotDefaultConstructable {
NotDefaultConstructable(int){};
};
int main() {
std::array<NotDefaultConstructable, 3> arr{1, 2, 3};
}
In this sense, all elements of a std::array object should arguably be initialized, even if they represent a non-present object (yet to be "filled", if you will).
You could either find a an appropriate static vector container, such as boost::static_vector, or you could e.g. implement a thin wrapper around std::array which stores an array as above as well its runtime size. Another alternative would be to use a std::array of optionals:
#include <array>
#include <iostream>
#include <optional>
struct NotDefaultConstructable {
NotDefaultConstructable(int val) : val_(val) {};
int val_;
};
int main() {
std::array<std::optional<NotDefaultConstructable>, 3> arr{};
arr[1] = NotDefaultConstructable{42};
for(const auto& element : arr) {
if(element.has_value()) {
std::cout << "has value: " << element.value().val_;
} // has value: 42
}
}
where no dynamic memory allocation takes place (cppreference):
If an optional contains a value, the value is guaranteed to be allocated as part of the optional object footprint, i.e. no dynamic memory allocation ever takes place.
Judging by the fact that OP mentioned looking into using placement new, I wanted to make a full example of using a stack allocated array as memory for dynamically constructed objects.
Using this method, you can allocate stack memory without initializing it with instances. The problem, however, is that you have to monitor the object lifetime for each instance yourself. In this example, I construct all of them and can then simply assume that all of them have to be destroyed, however monitoring how many instances are alive and should be destroyed is quite messy.
This is why dfrib's answer is much more suitable in your situation, as it allows you to allocate the memory on the stack using std::array<std::optional<Type>, 5> and assign instances later. Object lifetime will also be managed for you, so is much more advisable.
Example 1: Placement New:
#include <array>
#include <iostream>
struct NotDefaultConstructable {
int Value;
NotDefaultConstructable(int val) : Value(val) {
std::cout << "constructed: " << Value << "\n";
};
~NotDefaultConstructable(){
std::cout << "destructed: " << Value << "\n";
}
};
int main() {
// allocate enough memory on the stack for 5 instances
char aV[sizeof(NotDefaultConstructable) * 5];
// get a pointer to the first NotDefaultConstructable in that array
auto avArray = static_cast<NotDefaultConstructable*>(static_cast<void*>(&aV[0]));
// use placement new to construct each instance
for (auto i = 0; i < 5; ++i)
new (&avArray[i]) NotDefaultConstructable((i + 1) * 2);
// do stuff with the instances
for (auto i = 0; i < 5; ++i)
std::cout << "instance: " << avArray[i].Value << "\n";
// destruct them all manually, this is what makes placement new a little
// cumbersome. I would advise to use std::optional instead.
for (auto i = 0; i < 5; ++i)
avArray[i].~NotDefaultConstructable();
}
example 1: https://godbolt.org/z/7jW8Pb
Example 2: std::array with std::optional
Here's an example using std::optional, which has minor overhead (about ~4 bytes per object) to achieve much more convenience:
#include <array>
#include <optional>
#include <iostream>
struct NotDefaultConstructable {
int Value;
NotDefaultConstructable(int val) : Value(val) {
std::cout << "constructed: " << Value << "\n";
};
~NotDefaultConstructable(){
std::cout << "destructed: " << Value << "\n";
}
};
int main() {
// allocate enough memory on the stack for 5 instances
std::array<std::optional<NotDefaultConstructable>, 5> avArray;
// use placement new to construct each instance
for (auto i = 0; i < 5; ++i)
avArray[i] = NotDefaultConstructable((i + 1) * 2);
// do stuff with the instances
for (auto i = 0; i < 5; ++i)
std::cout << "instance: " << avArray[i].value().Value << "\n";
}
example 2: https://godbolt.org/z/Ynx8aE
You can try the following
#include <iostream>
struct NotDefaultConstructable {
NotDefaultConstructable(int val) {
};
};
int main() {
NotDefaultConstructable aV[5] { 6, 6, 6, 6, 6 };
}
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.
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;
}
I am trying to copy an array.
class Myobject
{
int nb;
string name;
Myobject* next;
Myobject(int nb, string name) {this->name=name; this->nb=nb; this->next=NULL;}
};
Myobject **array;
array= new Myobject*[100];
how can I make a deep copy of the Myobject ** and thus be able to modify one of the instances.
If you want to be up to date, use RAII for which C++ is really good for.
#include <vector>
#include <memory>
#include <string>
#include <iostream>
struct Myobject : std::enable_shared_from_this< Myobject >
{
int nb;
std::string name;
std::shared_ptr<Myobject> next;
Myobject(int nb, std::string name) { this->name=name; this->nb=nb; }
~Myobject() { std::cout << "deleting " << nb << " " << name << std::endl; }
};
int main() {
std::vector< std::shared_ptr< Myobject > > arr;
arr.push_back(std::make_shared< Myobject >(1,"first"));
arr.push_back(std::make_shared< Myobject >(2,"second"));
for (auto obj: arr) {
std::cout << obj->nb << " " << obj->name << std::endl;
}
arr[0]->next = arr[1];
};
outputting
1 first
2 second
deleting 1 first
deleting 2 second
and you don't need to care much about memory management at first. Your example without proper care would generate memory leaks.
Update: forgot to mention, if you have circular references, beware, you might get memory leaks using shared_ptrs as well. If that should be the case, you might avoid that by switching to weak_ptrs → std::weak_ptr
You may add this (deep)-copy constructor:
Myobject(const Myobject& rhs) :
nb(rhs.nb),
name(rhs.name),
next(rhs.next == NULL ? NULL : new Myobject(*rhs.next))
{}
You will probably have to add a correct destrutor to avoid memory leak...
If possible, I suggest to use std::list<Myobject> instead
Allocate array and loop to copy.
NewMyobject **newarray = new Myobject*[100];
for (int i = 0 ; i < 100; ++i)
newarray[i] = new MyObject(array[i]);
Object must have a copy constructor.