I am fairly new at the C/C++ pointer stuff, so I was wondering if this could cause any problems?
I got a class with a pointer array variable, which I would like to move along with the other variables in the move constructor. Can I just reasign its pointer, or is that a bad idea?
class C {
private:
char* a;
int size;
public:
C(int s)
{
size = s;
a = new char[s];
}
~C() noexcept
{
size = 0;
delete[] a;
}
C(const C& o): C(o.size)
{
std::copy(o.a, o.a + size, a);
}
C(C&& o) noexcept: size(o.size)
{
a = o.a;
o.a = nullptr;
}
C& operator=(const C& o)
{
if(this != &o)
{
char * l = new char[o.size];
size = o.size;
delete[] a;
a = l;
std::copy(o.a, o.a + size, a);
}
return *this;
}
C& operator=(C&& o) noexcept
{
if(this != &o)
{
delete[] a;
size = std::move(o.size);
a = o.a;
o.a = nullptr;
}
return *this;
}
};
Any feedback is welcome!
EDITS
Added move & assignment operator and copy constructor for complecity sake
Updated for full 'working' MVP
Why don't you use std::string and let the compiler do the work for you?
class C {
private:
std::string a;
public:
C() {}
}
Related
I've been banging my head on a crash that has to do with copy assignment of an object that holds a vector of unique_ptr that point to a class that hides std::vector data behind a void *. I have taken care of writing copy operator overloads so that the unique_ptr objects are copied correctly along their owning object.
Below is a minimal example of the issue. When executing the code, a crash relating to undefined memory happens when A is copied into B, the DataHolderImpl class is fed corrupted memory in it's set() method during the cloning of the object.
Any help would be really appreciated, this seems out of my depth and I'm not sure what to look for at this stage. Thanks !
#include <memory>
#include <vector>
#include <cstring>
class DataHolderBase {
public:
DataHolderBase() {};
virtual ~DataHolderBase() {};
virtual std::unique_ptr<DataHolderBase> clone() const = 0;
};
class DataHolderImpl : public DataHolderBase {
public:
DataHolderImpl() : DataHolderBase() {
m_data = new std::vector<float>;
}
~DataHolderImpl() {
delete reinterpret_cast<std::vector<float> *>(m_data);
}
std::unique_ptr<DataHolderBase> clone() const override {
DataHolderBase * copy = new DataHolderImpl(*this);
return std::unique_ptr<DataHolderBase>(copy);
}
void setVector(std::vector<float> const & data) {
if(m_data) {
delete reinterpret_cast<std::vector<float> *>(m_data);
}
m_data = (void *)malloc(sizeof(float) * data.size());
memcpy(m_data, data.data(), sizeof(float) * data.size());
}
void set(void * data) {
std::vector<float> * vec = reinterpret_cast<std::vector<float> *>(data);
setVector(*vec);
}
void* get() const {
return m_data;
}
DataHolderImpl(DataHolderImpl const & other) :
DataHolderBase(other),
m_data(nullptr)
{
set(other.get());
}
DataHolderImpl(DataHolderImpl && other) = default;
DataHolderImpl& operator=( DataHolderImpl const & other )
{
if(this != &other) {
set(other.get());
}
return *this;
};
DataHolderImpl& operator=( DataHolderImpl&& other ) = default;
private:
void * m_data;
};
class MyObject {
public:
MyObject() {
m_dataHolders.push_back(std::make_unique<DataHolderImpl>());
};
~MyObject() {};
MyObject(MyObject const & other)
{
m_dataHolders = std::vector<std::unique_ptr<DataHolderBase>>();
m_dataHolders.reserve(other.m_dataHolders.size());
for(auto const & dataHolder : other.m_dataHolders) {
m_dataHolders.push_back(dataHolder->clone());
}
}
MyObject(MyObject && other) = default;
MyObject& operator=( MyObject const & other )
{
if (this != &other)
{
m_dataHolders = std::vector<std::unique_ptr<DataHolderBase>>();
m_dataHolders.reserve(other.m_dataHolders.size());
for(auto const & dataHolder : other.m_dataHolders) {
m_dataHolders.push_back(dataHolder->clone());
}
}
return *this;
};
MyObject& operator=( MyObject&& other ) = default;
private:
std::vector<std::unique_ptr<DataHolderBase>> m_dataHolders;
};
class Test {
public:
Test() : m_A(), m_B()
{
m_B = m_A;
}
private:
MyObject m_A;
MyObject m_B;
};
int main(int argc, char * argv[]) {
auto A = Test();
auto B = A; // <- Crashes here.
return 0;
}
How can I delete all the pointer in my vector before that I going to copy to my vector new pointers (in case of const object)?
class Element {
int val;
public:
Element(int val) :
val(val) {
}
};
class MyClass {
vector<Element*> v;
static void FreeMemory(vector<Element*>& vec) {
for (unsigned int i = 0; i < vec.size(); i++) {
delete vec[i];
}
}
public:
MyClass() = default;
MyClass(const MyClass& mc) {
//...
}
MyClass& operator=(const MyClass& mc) {
if (this == &mc) {
return *this;
}
//...
FreeMemory(mc.v); //**error** - how can I delete the memory of any Element?
//...
return *this;
}
// ....
// ....
};
binding 'const std::vector' to reference of type 'std::vector&' discards qualifiers
Can I fix it with smart_pointers?
Here is a sketch of a class owning a std::vector of Element objects managed by std::unique_ptr. Not fully worked out, but it might work as a starting point.
class Element {
int val;
public:
explicit Element(int val) : val(val) {}
virtual ~Element() = default;
/* Subclasses must implement this method: */
virtual std::unique_ptr<Element> clone() const = 0;
};
class ExclusivelyOwning {
private:
std::vector<std::unique_ptr<Element>> v;
public:
ExclusivelyOwning() = default;
ExclusivelyOwning(const ExclusivelyOwning& other) {
v.reserve(other.v.size());
for (const auto& element : other.v)
v.push_back(element->clone());
}
ExclusivelyOwning& operator=(const ExclusivelyOwning& rhs) {
/* Use copy-swap idiom, not shown here. */
return *this;
}
ExclusivelyOwning(ExclusivelyOwning&&) = default;
ExclusivelyOwning& operator = (ExclusivelyOwning&&) = default;
~ExclusivelyOwning() = default;
};
Note that copying an object with references to abstract classes (as Element in the code above) requires further techniques to render the copying meaningful. Here, I used a "virtual constructor" - the clone method.
I got this program in C++ and it works perfectly fine if I put last 2 lines in comment:
a = b;
MyAr c(b);
I tested it with a method that I deleted it. The problem is with the *this pointer I think, when I compile and run the program flashes for a moment and then disappears. Can you please help me? Thanks!
#include <iostream>
using namespace std;
class MyAr {
int *p;
int len;
public:
MyAr();
MyAr(int a);
MyAr(const MyAr& ob);
~MyAr();
MyAr& operator=(const MyAr& ox) { *this = ox; }
int& operator[](int i) { return p[i]; }
int length();
};
MyAr::MyAr() : p(0), len(0) {}
MyAr::MyAr(int a) : p(new int[a]), len(a) {}
MyAr::MyAr(const MyAr& ob) { *this = ob; }
MyAr::~MyAr() { delete p; }
int MyAr:: length(){
return len;
}
int main(){
MyAr a;
MyAr b(10);
for(int i=0; i< b.length(); ++i)
b[i] = i;
a = b;
MyAr c(b);
system("pause");
return(0);
}
The definition
MyAr& MyAr::operator=(const MyAr& ox) { *this = ox; }
is recursive, since the assignment *this = ox calls the overloaded assignment operator again. So you have infinite recursion (leading to the eventual termination of your program, presumably).
It's the same as calling the following function:
void f() { f(); }
Or, in English, you've defined the meaning of "assign from value ox" to be "assign from value ox", when what you really need to do is to define what it should mean in terms of the constituent structure of your type!
For example:
MyAr& MyAr::operator=(const MyAr& ox) {
delete [] a;
a = nullptr;
len = 0;
return *this;
}
(This may not have the semantics you desire; modify to taste.)
One of the most important things you will ever do in c++ is learn to write correct constructors and destructors:
#include <cassert>
#include <cstring>
#include <utility>
class MyAr {
int *p;
int len;
public:
MyAr() : p(nullptr), len(0) {};
MyAr(int a) : p(new int[a]), len(a) {};
// because we are overloading the destructor, rule of 3 is required (c++03)
MyAr(const MyAr& ob)
: p( nullptr), len(ob.len)
{
if (len) {
assert(ob.p);
p = new int[len];
std::memcpy(p, ob.p, len);
}
}
MyAr& operator=(const MyAr& r)
{
MyAr tmp(r);
swap(tmp);
return *this;
}
~MyAr() {
// note: delete []
delete [] p;
}
// or rule of 5 (c++11)
#if __cplusplus >= 201103L
MyAr(MyAr&& r)
: p(r.p)
, len(r.len)
{
r.p = nullptr;
r.len = 0;
}
MyAr& operator=(MyAr&& r)
{
auto tmp = MyAr(std::move(r));
swap(tmp);
return *this;
}
#endif
void swap(MyAr& other)
{
using std::swap;
swap(p, other.p);
swap(len, other.len);
}
int& operator[](int i) { return p[i]; }
int length();
};
I would like to further exhaust this topic.
Assume that I have something like:
class MyClass
{
public:
MyClass(int N)
{
data_ptr = new float[N];
};
float* dat_ptr;
// ... clever operator definition here ...
};
So I would like to be able to simply write:
MyClass a(4);
MyClass b(4);
MyClass c(4);
// modify b.data_ptr and c.data_ptr ....
// Use "clever operator"
a = b + c;
Where the operator would do a.data_ptr[i] = b.data_ptr[i] + c.data_ptr[i] for i=0:(N-1) ...
Hence no extra copies of the data are created and we are neatly using the preallocated buffers.
Is this possible? If so, please provide me with som insights as to how it would be done.
Thanks!
You can, if you use move semantics from C++11.
class MyClass
{
public:
MyClass(int N)
{
data_ptr = new float[N];
n = N;
}
MyClass(MyClass && rhs)
{
data_ptr = rhs.data_ptr;
n = rhs.n;
rhs.data_ptr = nullptr;
}
// dtor, copy-ctor etc.
int n;
float * dat_ptr;
};
MyClass operator + (const MyClass & left, const MyClass & right)
{
MyClass result(left.n);
// Implement addition
}
// Note: no error-checking
This way a temporary object will be created, but the internal data will not be unnecessarily copied.
Read more about the move semantics.
It is not possible; Before a is assigned to, a temporary object will be created as a result of calling operator + (b, c); This operator should return the created instance, that should then be assigned to a; the created instance is always created by b + c.
What is possible though is to define += as a member operator and say:
b += c;
This would modify the value of b without creating extra copies.
Edit: I have reconsidered :)
You definitely can do it, by abstracting operations as lazy evaluation objects.
Here is an example:
class MyClass; // fwd. declaration of your class
struct LazySum
{
LazySum(const MyClass& a, const MyClass& b)
: x(a), y(b) {}
float operator[](int i) { return x[i] + y[i]; }
const MyClass& x;
const MyClass& y;
};
class MyClass
{
public:
MyClass(int N)
{
data_ptr = new float[n = N];
};
int n; // this shouldn't be public
float* dat_ptr; // nor this, but I went with your code
// ... clever operator definition here ...
MyClass& operator=(const LazySum& terms)
{
// ignore case when n != x.n or n != y.n
// because not the point of the example
// (and I'm lazy)
// sum evaluation occurs here
// with no new allocations
for(int i = 0; i < n; ++i)
data_ptr[i] = terms[i];
return *this;
}
};
LazySum operator=(const MyClass& x, const MyClass& y)
{
return LazySum(x, y); // LazySum is a couple of references in size
}
void client_code_using_clever_op()
{
MyClass a(4);
MyClass b(4);
MyClass c(4);
// modify b.data_ptr and c.data_ptr ....
// Use "clever operator"
a = b + c; // actual sum performed when operator = is executed
}
The idea is to store the terms, and perform late evaluation on the terms.
Points of improvement:
inject a functor in the construction of LazySum to make it become LazyOp (the functor would decide what the op is); Implement other binary operators on MyClass in terms of it.
use RAII in MyClass.
when you need to implement lazy evaluation operators on another type (e.g. some MyOtherClass) consider implementing LazyOp as a template on the terms and functor type.
this does not support more complex expressions without some extra work:
MyClass a(4), b(4), c(4), d(4);
d = (a + b) + c; // error
This example will not work because it would require an operator+(const LazySum&, const MyClass&);;
As Spook explained, yes it is possible. Just for fun I wrote a full example that you can compile and run. If a copy was to be created, you would get a message in the output. I tried this example in Visual Studio 2012 and runs fine.
class MyClass
{
private:
float *data_ptr;
std::size_t size;
public:
MyClass(std::size_t N = 0) :
size(N),
data_ptr(N ? new float[N]() : nullptr)
{}
MyClass(const MyClass& other) :
size(other.size),
data_ptr(other.size ? new float[other.size]() : nullptr)
{
std::copy(other.data_ptr, other.data_ptr + size, data_ptr);
std::cout << "Copy!" << std::endl;
}
MyClass(MyClass&& other)
{
size = 0;
data_ptr = nullptr;
swap(*this, other);
}
~MyClass()
{
delete[] data_ptr;
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
friend MyClass operator+(MyClass& first, MyClass& second)
{
MyClass result(std::min(first.size, second.size));
for (std::size_t i=0; i < result.size; i++) {
result.data_ptr[i] = first.data_ptr[i] + second.data_ptr[i];
}
return result;
}
friend void swap(MyClass& first, MyClass& second)
{
std::swap(first.size, second.size);
std::swap(first.data_ptr, second.data_ptr);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
MyClass a(5);
MyClass b(5);
MyClass c(5);
a = b + c; //this should not produce an extra copy
return 0;
}
class A {
public:
void foo()
{
char *buf = new char[10];
vec.push_back(buf);
}
private:
vector<char *> vec;
};
int main()
{
A a;
a.foo();
a.foo();
}
In class A, foo() allocates some memory and the pointer is saved to vec. When main() finishes, a will desconstruct, and so will a.vec, but will the allocated memory be released?
The memory will not be released. For it to be released, you need to put it in either a unique_ptr or a shared_ptr.
class A {
public:
void foo()
{
unique_ptr<char[]> buf(new char[10]);
vec.push_back(buf);
}
private:
vector<unique_ptr<char[]>> vec;
};
Or you could make a destructor
~A()
{
for(unsigned int i =0; i < vec.size(); ++i)
delete [] vec[i];
}
EDIT
As pointed out you need to make copy and assignment also (if you intend to use them that is)
class A
{
public:
A& operator=(const A& other)
{
if(&other == this)
return *this;
DeepCopyFrom(other);
return *this;
}
A(const A& other)
{
DeepCopyFrom(other);
}
private:
void DeepCopyFrom(const A& other)
{
for(unsigned int i = 0; i < other.vec.size(); ++i)
{
char* buff = new char[strlen(other.vec[i])];
memcpy(buff, other.vec[i], strlen(other.vec[i]));
}
}
std::vector<char*> vec;
};
More on the subject of deep copying and why you need it here
http://www.learncpp.com/cpp-tutorial/912-shallow-vs-deep-copying/