So I have a storage class that has a ton of basic functionality that is really useful as is. It has move constructors to allow return by value.
class A
{
public:
virtual ~A(){}
A(const A& a);
A(A&& a);
A& operator=(const A& rhs);
A& operator=(A&& rhs);
int foo();//example member function
//example function returning by value, elision and RHR make it efficient
static A Foo();
};
This is great because it allows who own A to be very well defined. If I start needed to have inherited classes that extend A, AND the return statement of a call function to have polymorphism, is the only "correct" way to use smart pointers? AKA
class B_interface : public A
{
public:
virtual ~B_interface(){}
virtual void FooBar() = 0;
};
class B : public B_interface
{
public:
virtual ~B(){}
B(const B& a);
B(B&& a);
B& operator=(const B& rhs);
B& operator=(B&& rhs);
virtual void FooBar() override;
static shared_ptr<B> idontlikeit();
}
I thought of a (probably bad) way to get around it: if, instead of inheritance, use composition: the class contains something akin to a impl ptr:
class B_interface
{
public:
virtual void FooBar() = 0;
};
class B : public A
{
shared_ptr<B_interface> impl_;//could use
public:
B(const A& a,shared_ptr<B_interface> interface)//or some smarter way to pass this in
: A(a)//call a's copy constructor
{
impl_.reset(interface);
}//this constructor thinks
void FooBar() { impl_->FooBar();}
virtual ~B(){}
B(const B& a);
B(B&& a);
B& operator=(const B& rhs);
B& operator=(B&& rhs);
static B ilikeitbetter();
}
So I like using the class better with that one, but making the class kinda stinks with that one...also, the B_interface might not make much sense outside B...Do you guys have any alternatives?
Yes, runtime polymorphism requires dynamic allocation and pointers. Derived classes can be (and usually are) of different size than base classes, so code expecting value semantics would not know how large a block to reserve for the value type.
In this specific case, consider returning a unique_ptr<Interface> if you don't expect the caller to require shared semantics. That way you don't force the caller to do reference counting if they don't need it. (The caller can always transfer ownership from the unique_ptr to a shared_ptr if they want shared semantics)
Related
So let's say i have interface:
class MyInterface
{
public:
virtual ~MyInterface() = default;
virtual void DoSomething(const MyType& a, const MyTypeB& b);
};
And the thing i want is to allow usage of move semantics if any of function arguments is rvalue or lvalue reference.
What i don't want is to define interface like this:
class MyInterface
{
public:
virtual ~MyInterface() = default;
virtual void DoSomething(const MyType& a, const MyTypeB& b);
virtual void DoSomething(MyType&& a, const MyTypeB& b);
virtual void DoSomething(const MyType& a, MyTypeB&& b);
virtual void DoSomething(MyType&& a, MyTypeB&& b);
};
And the combinatorics get even worse if more parameters added to the method.
So in implementation i basically want to move argument if i got rvalue passed and copy otherwise.
There's std::forward in the standard library, but it works with so called "forwarding reference", which require templates and it's impossible to have template parameters in virtual methods.
Is there any way to do this with preserving the purpose of interface base type and without bloating the interface itself so much?
Is there any reason to make the op== in a C++ base class non-virtual, i.e., is there any downside to this implementation:
class A {
public:
virtual bool operator== (const A& rhs) { ... }
};
class B : public A {
public:
bool operator== (const A& rhs) override { /* cast rhs to const B& */ {}
};
In my book they recommend to make the op== non-virtual and let it call a protected virtual member function which performs the polymorphism instead. It doesn't say anything about why they don't use the op== directly like I do in my example above. Is there a good reason I'm overlooking?
There is well-known clone idiom for copying Derived objects via pointer to Base class.
class Base{
int b;
public:
virtual unique_ptr<Base> clone() const = 0;
virtual ~Base() = default;
};
class Derived : public Base {
int d;
public:
virtual unique_ptr<Base> clone() const override {
return std::make_unique<Derived>(*this);
}
}
However, I can't find clear instructions how to define copy constructors and assignments in this case. This is how I suppose it should be done in Base class:
class Base {
protected:
Base(const Base&) = default;
private:
Base& operator=(const Base&) = delete;
}
Is it necessary (in order to avoid potential slices)? Is it right way to do it? Does it suffice or should I add such declarations to Derived class as well?
As derived classes use the copy constructor to create clones, you may like to make the copy constructors non-public to avoid accidental slicing but accessible to derived classes.
protected fills this requirement. It needs to be applied to each class' copy constructor because the compiler-generated copy constructor is public. It also makes sense to apply the same treatment to the assignment operator.
That also prevents std::make_unique from accessing the copy constructor though:
class A
{
protected:
A(A const&) = default;
A& operator=(A const&) = default;
public:
A();
virtual std::unique_ptr<A> clone() const = 0;
};
class B : public A
{
protected:
B(B const&) = default;
B& operator=(B const&) = default;
public:
B();
std::unique_ptr<A> clone() const override {
return std::unique_ptr<A>(new B(*this));
}
};
Deleting the copy assignment operator is probably a good idea, unless you need it.
Deleting operator=(const Base&) in Base is enough, as the implicitly declared copy assignment operator is defined as deleted for a derived class if the base class has no copy assignment operator (see cppreference.com).
If you really want copy assignment you can make the copy assignment operator virtual, and carefully implement the correct behaviour in the derived classes by
calling Base::operator= to assign the Base class members, and
assigning the members of the derived class, using dynamic_cast to ensure that the argument is of the correct type .
If done correctly, this avoids object slicing and retains the correct type.
An example (with copy constructor details omitted):
struct Point {
virtual Point& operator=(const Point& p) =default;
int x;
int y;
};
struct Point3d :public Point{
virtual Point3d& operator=(const Point& p);
int z;
};
Point3d& Point3d::operator=(const Point& p)
{
Point::operator=(p);
auto p3d = dynamic_cast<const Point3d*>(&p);
if(p3d){
z = p3d->z;
} else {
z = 0;
}
return *this;
}
Assume we have this two classes, with implemented swap-idioms. The copy constructor and assignment operator of the base class are deleted, as it makes no sense. However the swap-method is implemented, as it holds a member.
namespace std
{
template<>
void swap<Base>(Base& a, Base& b)
{
a.swap(b);
}
template<>
void swap<Derived>(Derived& a, Derived& b)
{
a.swap(b);
}
}
class Base
{
public:
Base(int ID) : ID_(ID) {}
virtual std::string getString()=0;
Base(const Base&)=delete;
operator=(const Base&)=delete;
void swap(Base& rhs)
{
std:swap(ID_, rhs.ID_);
}
private:
int ID_;
}
class Derived : public Base
{
public:
Derived(int ID, bool Value) : Base(ID), Value_(Value) {}
virtual ~Derived() {}
Derived(Derived& rhs)
{
std::swap(*this, rhs);
}
virtual std::string getString() {return Value_ ? "True" : "False"}
void swap(Derived& lhs, Derived& rhs)
{
std::swap(static_cast<Base&>(lhs), static_cast<Base&>(rhs);
std::swap(lhs.Value_, rhs.Value_);
}
private:
bool Value_;
}
As seen in many examples, this would be the standard way to do it I suppose.
However, I see a problem with the public Base::swap, as it should not be possible to swap only the abstract base-class!
Wouldn't it be better to remove the template for the Base class and make the Base::swap Method protected:
class Base
{
...
protected:
void swap(Base& rhs, Base &lhs);
}
class Derived : public Base
{
...
public:
void swap(Derived& lhs, Derived& rhs)
{
Base::swap(static_cast<Base&>(lhs), static_cast<Base&>(rhs);
std::swap(lhs.Value_, rhs.Value_);
}
}
Assuming, there is another class derived from base, with the first implementation it would be possible to swap the ID, however the data members of the derived objects would stay the same.
So, am I right thinking that swapping of a abstract class should not be possible from outside?
The copy constructor and assignment operator of the base class are deleted, as it makes no sense.
Actually, this is terrible. Now you made Derived uncopyable! Why? There is no reason to add this restriction. The default copy constructor and assignment operator of Base are perfectly reasonable in the context of copying the most-base class of the hierarchy.
Once you undo that, there's no need to do anything else as std::swap(derived1, derived2) would already do the right thing. The default move construction/operation is correct. It's always good to let the compiler do things for you.
But if you want to override swap anyway, the correct way to do that would be as a non-member friend:
class Base {
...
friend void swap(Base& lhs, Base& rhs) {
using std::swap;
swap(lhs.ID_, rhs.ID_);
}
};
class Derived : public Base {
...
friend void swap(Derived& lhs, Derived& rhs) {
using std::swap;
swap(static_cast<Base&>(lhs), static_cast<Base&>(rhs));
swap(lhs.Value_, rhs.Value_);
}
};
Also, your Derived copy constructor makes no sense. Remove it as per the first paragraph of my answer.
I have two classes, from two different libraries, with the same meaning:
class A {
public:
A() {}
A(const A&) {}
};
class B {
public:
B() {}
B(const B&) {}
};
I want to call functions with B as parameter, passing an A object:
void setB(const B&) {
}
int main(int argc, char* argv[]) {
A a;
setB(a);
}
I know this is possible, adding a conversion contructor from:
class B {
public:
B() {}
B(const B&) {}
B(const A&) {} // CANNOT ADD THIS!
};
But these classes are defined in two external libraries and I haven't rights to change them.
How can I add a conversion constructor (or achieve the same result) without touching the B class definition?
How can I add a conversion constructor (or achieve the same result) without touching the B class?definition?
If the B class has a virtual destructor, you can specialize B and add the extra functionality in the specialization. If it does not have a virtual destructor, you can embed B in a wrapper class.
That means either:
class SpecialB: public B { // if B has virtual destructor
public:
SpecialB(const A&); // you _can_ do this
};
or:
class SpecialB {
public:
SpecialB(const A&); // you _can_ do this
private:
B wrapped_b;
};
Just use a factory:
struct AfromB
{
static A convert(const B&);
}
and:
setB(AfromB::convert(a));
If you have no access to B class definition, maybe the simpler is to have a void setB(const A&) overload.