How can I properly implement a copy constructor for my iterator, knowing that the pointer to the data object is private? Do I need to create an accessor for this pointer? Isn't this a bad thing?
Since you define a copy constructor for an iterator inside the implementation of the iterator itself, there is no need to make the hidden pointer accessible, with or without an accessor: the copy constructor will have access to it even if it is private, so there's nothing to worry about:
template <class T>
class MyIter {
T *private_ptr;
public:
MyIter(const MyIter<T> &other) : private_ptr(other.private_ptr) {}
... // More constructors and member functions
};
All member functions of a class have access to the data members of the class. This also applies to different objects of the same type.
class Foo()
{
private:
int bar;
public:
void foobar(Foo & different) { bar = different.bar; }
};
Related
I understand that if I have a class with a smart unique pointer, it is not possible to assign that class to another instance, as a unique pointer cannot be copied. I understand that I could make the unique pointer a shared pointer and this would solve the problem. But what if I did not want to share ownership of the pointer? Is it possible to create an assignment operator that moves the unique pointer and copies the other variables?
I have read that you can use std::move to pass ownership.
#include <iostream>
#include <memory>
struct GraphStructure { };
class test {
int a;
std::vector<int> vector;
std::unique_ptr<GraphStructure> Graph_;
};
int main () {
test t1;
auto t2 = t1;
}
The default copy constructor of class test is deleted because of a member (graph_) not being copiable (if you still could copy in any meaningful way, e. g. by creating a deep copy of the graph member, you'd have to implement on your own copy constructor). In contrast, the default move constructor still exists (std::unique_ptr is movable). So what you can do is the following:
test t1;
auto t2 = std::move(t1);
Be aware, though, that t1 then won't hold any object any more (you moved the object, so you moved its contents to another one) and the object previously held by t2 is destroyed. If this is a meaningful state is up to you to decide...
Side note: What I wrote about copy and move constructors applies for copy and move assignment as well...
Fixing this the easy way
If GraphStructure is a class or struct without any virtual member functions, this is easy to do. We can write a function to duplicate the data inside a unique_ptr to create a new GraphStructure:
std::unique_ptr<GraphStructure> duplicate(std::unique_ptr<GraphStructure> const& ptr)
{
return std::make_unique<GraphStructure>(*ptr);
}
Once we have duplicate, we can use this class to write a copy constructor for test:
class test {
std::unique_ptr<GraphStructure> ptr;
std::vector<int> values;
public:
// this can be defaulted
test() = default;
// we use duplicate to create a copy constructor
test(const test& source)
: ptr(duplicate(source.ptr)))
, values(source.values)
{}
// we can use the default move constructor
test(test&&) = default;
test& operator=(test const& source) {
ptr = duplicate(source.ptr);
values = source.values;
return *this;
}
// we can use the default move assignment operator
test& operator=(test&&) = default;
};
What if GraphStructure has virtual methods?
In this case, add a virtual clone method to GraphStructure that returns a new std::unique_ptr<GraphStructure>:
class GraphStructure {
public:
// override this method in child classes
virtual std::unique_ptr<GraphStructure> clone() {
return std::make_unique<GraphStructure>(*this);
}
virtual ~GraphStructure() {}
};
Then, use .clone() in place of duplicate
I didn't think it was possible, but if you have two instances of the same class, are you allowed to access one's private members from the other?
Is this why you can also do this in copy constructors? in fact, is the copy constructor the reason this is allowed? Doesn't this break encapsulation?
Yes, any code within a class can access private data in any instance of the class.
This breaks encapsulation if you think of the unit of encapsulation as the object. C++ doesn't think of it that way; it thinks of encapsulation in terms of the class.
Access restrictions are a property of the class, not of an instance.
That's why you can write your usual copy constructor:
class Foo
{
int a; // private!
public:
Foo (Foo const & rhs) : a(rhs.a) { } // rhs.a is accessible
};
This idea is also what fuels the "factory" idiom:
class Bar
{
Bar() { } // private?!
public:
static Bar * create() { return new Bar(); } // Bar::Bar() is accessible
};
I am overloading operator= on a struct EqualTestBase, and operator= takes different parameters than are used to construct the struct.
struct EqualTestBase
{
EqualTestBase(int one) {}
EqualTestBase& operator=(std::string two)
{
//stuff
return *this;
}
};
It works fine on the base class. But a trival struct derived from it, EqualTestDerived, acts like it doesn't have the operator= member function.
struct EqualTestDerived : public EqualTestBase
{
EqualTestDerived(int one) : EqualTestBase(one) {}
};
void test()
{
EqualTestBase basetest(0);
basetest = "test"; //this is fine, compiles
EqualTestDerived derivedtest(0);
derivedtest = "test"; //this does not compile, says there is no constructor that takes type const char[5]
}
Do I have to redefine operator= on all derived structs, or is there a way to automatically pass down that functionality?
The derived class has an implicitly-declared copy-assignment operator, which hides the one declared in the base class. You can use using to bring it into scope:
struct EqualTestDerived : public EqualTestBase
{
EqualTestDerived(int one) : EqualTestBase(one) {}
using EqualTestBase::operator=;
};
operator= isn't inherited. If a class doesn't define operator= itself, the compiler will synthesize one for it (regardless of the fact that its base class does define an operator=).
If you want something that can be inherited (and can be virtual) you generally want to define it as a function with a different name. The generally accepted name for such a function is clone (though clone is normally more like a copy constructor, creating a new instance of an object, not just assigning to an existing one).
I just need to know if I want to call my copyconstuctor from pImpl class, how will I do it?
For example:
CImpl::SomeFunc()
{
//cloning the caller class instance
caller = new Caller(*this)// I cant do this since its a pImpl class
}
How can i achieve this?
Well after reading your comments, it seems that you want to be able to provide the ability to make copies of Caller class. If so, then in that case you should implement the copy constructor for Caller class, where you can make a hard copy of m_pImpl pointer.
class CallerImpl;
class Caller
{
std::shared_ptr<CallerImpl> m_pImpl;
public:
Caller(Caller const & other) : m_pImpl(other.m_pImpl->Clone()) {}
//...
};
And then you can implement Clone() function in CallerImpl class as:
class CallerImpl
{
public:
CallerImpl* Clone() const
{
return new CallerImpl(*this); //create a copy and return it
}
//...
};
Now you can make copy of Caller:
//Usage
Caller original;
Caller copy(original);
I have these classes:
class Base
{
...
private:
std::vector<X> v;
};
class Derived : public Base
{
Derived(X*, int n);
};
where the constructor of Derived is passed an array of item Xs, which I need to insert into my vector v in the Base class. (X is a smart pointer)
Currently I see two ways to do this:
Create a function in Base:
InsertItem(X*) that will insert an
item into the vector (1 by 1)
Create a vector in Derived that contains the
full list, then get it into Base by
moving the entire vector.
I dont see any advantages to #2, but was wondering if #1 was a good solution, or if there are better ways to do this.
Thanks!
There is a third option if you can modify Base. You could make the vector protected which will allow base classes full access:
class Base
{
...
protected:
std::vector<X> v;
};
Your base class can now directly operate on v. If you need to expose much of the vector functionality to Derived, this is the easiest way.
If not, I would just add the one function (i.e. option #1) and would make it protected:
class Base
{
...
protected:
void push_back(const X &x)
{
v.push_back(x);
}
private:
std::vector<X> v;
};
Another option is to create a (public or protected) constructor in base that will take the parameters and populate the vector directly. No intermediate vectors need be created or passed around.
class Base
{
. . .
protected:
Base(X* x,int n);
. . .
};
class Derived : public Base
{
Derived(X* xes, int n):Base(xes,n){};
};
If you can, I would create a protected constructor in Base that accepts the X* argument just like the Derived constructor, and then in Derived's constructor initialization list, pass the argument to Base's protected constructor and have it do the actual insertion.
If you want the container in the base class to remain private, you might consider adding a public (or protected) function in Base to allow the derived class to add a range. This way, the Base container type can be encapsulated (Derived doesn't need to know it's a vector), and Derived can still pass the whole thing down in one shot:
class Base
{
...
private:
std::vector<X> v;
protected
template <class iterator_t>
void assign( iterator_t first, iterator_t last) {
v.assign( first, last);
}
};
class Derived : public Base
{
Derived(X* p, int n) {
Base::assign( p, p+n);
}
};
It's not much of an encapsulation, but the container could be changed with a changing the derived class, and a derived class could easily be written that takes data in a form different than an array, if desired. And as zdan suggested this functionality could be part of public or protected constructor for Base if that makes more sense (which is likely).
The idea of using iterator ranges to represent the content or subset of a container is widespread in the STL.
You should set the member variable to be protected so that classes that inherit Base can access it. Alternatively keep the member variable private but add a protected accessor method to allow classes that inherit base to add to the vector etc