Is removing this 2nd copy operation negligible - c++

Am I correct in thinking that 2 copies of Bar are performed when creating an instance of Foo?
struct Foo {
Foo(Bar a) {
a = a; // 2nd copy Bar performed
}
private:
Bar a;
};
Foo f(myBar); // 1st copy of Bar performed
Would making the constructor parameter a constant reference reduce the number of copies down to one. Would modern compilers detect these 2 copies and optimise for me? So in effect, me trying to optimise is pointless as I already achieve that optimisation implicitly. Also would a constructor initialiser list result in anything different?
Ie;
struct Foo {
Foo(const Bar& a) {
a = a; // copy made
}
private:
Bar a;
};
Foo f(myBar); // no copy made

For the 1st case:
struct Foo {
Foo(Bar b) {
a = b; // copy assignment operator called
}
private:
Bar a; // default ctor called
};
Foo f(myBar); // copy ctor called
For the 2nd case:
struct Foo {
Foo(const Bar& b) {
a = b; // copy assignment operator called
}
private:
Bar a; // default ctor called
};
Foo f(myBar); // nothing
And you can improve it further with member initialize list.
struct Foo {
Foo(const Bar& b) : a(b) { // copy ctor called
}
private:
Bar a; // nothing
};
Foo f(myBar); // nothing
PS: I fixed the parameter name as #teivaz commented.

Related

Idiomatic way to prevent slicing?

Sometimes it can be an annoyance that c++ defaults to allow slicing. For example
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
foo y = x; // <- I dont want this to compile!
}
This compiles and runs as expected! Though, what if I dont want to enable slicing?
What is the idiomatic way to write foo such that one cannot slice instances of any derived class?
I'm not sure if there is a named idiom for it but you can add a deleted function to the overload set that is a better match then the base classes slicing operations. If you change foo to
struct foo
{
int a;
foo() = default; // you have to add this because of the template constructor
template<typename T>
foo(const T&) = delete; // error trying to copy anything but a foo
template<typename T>
foo& operator=(const T&) = delete; // error assigning anything else but a foo
};
then you can only ever copy construct or copy assign a foo to foo. Any other type will pick the function template and you'll get an error about using a deleted function. This does mean that your class, and the classes that use it can no longer be an aggregate though. Since the members that are added are templates, they are not considered copy constructors or copy assignment operators so you'll get the default copy and move constructors and assignment operators.
Since 2011, the idiomatic way has been to use auto:
#include <iostream>
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
auto y = x; // <- y is a bar
}
If you wish to actively prevent slicing, there are a number of ways:
Usually the most preferable way, unless you specifically need inheritance (you often don't) is to use encapsulation:
#include <iostream>
struct foo { int a; };
struct bar
{
bar(int a, int b)
: foo_(a)
, b(b)
{}
int b;
int get_a() const { return foo_.a; }
private:
foo foo_;
};
int main() {
bar x{1,2};
// foo y = x; // <- does not compile
}
Another more specialised way might be to alter the permissions around copy operators:
#include <iostream>
struct foo {
int a;
protected:
foo(foo const&) = default;
foo(foo&&) = default;
foo& operator=(foo const&) = default;
foo& operator=(foo&&) = default;
};
struct bar : foo
{
bar(int a, int b)
: foo{a}, b{b}
{}
int b;
};
int main() {
auto x = bar (1,2);
// foo y = x; // <- does not compile
}
You can prevent the base from being copied outside of member functions of derived classes and the base itself by declaring the copy constructor protected:
struct foo {
// ...
protected:
foo(foo&) = default;
};

Calling a member constructor in class definition with a deleted copy constructor and initializer list overload

struct Foo
{
Foo(const Foo&) = delete;
Foo(int a, int b);
Foo(std::initializer_list<int>);
};
struct Bar
{
Foo f = Foo(1, 2);
// Foo f = {1, 2} - calls the initializer_list overload
};
How to initialize Foo with two ints if the copy constructor is deleted?
In order for that initialization to work, the type in question has to be MoveConstructible*. In your particular case, providing a move constructor would satisfy this requirement:
Foo(Foo&&) = default;
If this is not an option, you could initialize the member in a default constructor, and use it as a delegating constructor in other constructors.
struct Bar
{
Bar() : f(1, 2) {}
Bar(const FooBar&) : Bar() {}
Bar(double x) : Bar() {}
Foo f;
};
* This does not mean a copy will be made. T t = T() is an easy candidate for copy elision. However, a viable constructor must be accessible.

Getting the move constructor/assignment operator to be used

Consider the following code:
#include <iostream>
#define P_(x) std::cout << x << std::endl
class B {
public:
B() { P_("B::B()"); }
B(const B&) { P_("B::B(const B&)"); }
B(B&&) { P_("B::B(B&&)"); }
~B() { P_("B::~B()"); }
B& operator=(const B&) { P_("B::op=(const B&)"); return *this; }
B& operator=(B&& b) { P_("B::op=(B&&)"); return *this; }
};
class Foo {
public:
void setB(const B& b) { mB = b; }
private:
B mB;
};
B genB() {
return B();
}
int main() {
Foo f;
f.setB(genB());
}
Suppose B is a type that is difficult to copy-construct. I'd like to generate some B (with the function genB) and store it in a Foo. Since genB returns a temporary result, I'd expect that a move constructor would be used.
However, when I run the code, I get this output:
B::B()
B::B()
B::op=(const B&)
B::~B()
B::~B()
This clearly shows that two B's get created and destroyed, but that the second is a copy, and not a move of the first.
What is the best way to get move constructors used whenever possible?
Do I need to call std::move() somewhere?
Do I need a separate overload for a B& and a B&&?
Is there something else entirely that I'm missing?
You could overload that setB function:
class Foo {
public:
void setB(const B& b) { mB = b; }
void setB(B&& b) { mB = std::move(b); }
private:
B mB;
};
Or, you can use the "pass by value" way:
class Foo {
public:
void setB(B b) { mB = std::move(b); }
private:
B mB;
};
Here, the parameter b will be move constructed when possible or copy constructed otherwise.
The first B instance is the one created when creating your Foo instance :
Foo f;
This is because your Foo class has a B member called mB.
The second B instance is the one created by the genB() call.
The assignment operator is called because of the assignment you perform in the Foo::setB function :
mB = b;
Nowhere is there a chance to use a copy or move constructor.

copy constructor of a vector-derived class

I have the following classes.
class CA {
...
};
class CB: public vector<CA> {
...
};
How shall I implement the copy constructor of CB? i.e.,
CB(CB& cb).
How do I copy the content of the vector of cb in?
Ignoring the issue with publicly inheriting from an std::vector, if you really need a copy constructor for your derived type (for instance, if you are managing dynamically allocated resources), then you can use the constructor initialization list, as in this example, where Foo takes the place of your std::vector in the inheritance hierarchy:
#include <iostream>
struct Foo
{
Foo() {}
Foo(const Foo&) { std::cout << "Copy Foo\n";}
};
struct Bar : public Foo
{
Bar() {}
Bar(const Bar& b) : Foo(b) {}
};
int main()
{
Bar b1;
Bar b2(b1);
}

vector related memory allocation question

I am encountering the following bug.
I have a class Foo . Instances of this class are stored in a std::vector vec of class B.
in class Foo, I am creating an instance of class A by allocating memory using new and deleting that object in ~Foo().
the code compiles, but I get a crash at the runtime. If I disable delete my_a from desstructor of class Foo. The code runs fine (but there is going to be a memory leak).
Could someone please explain what is going wrong here and suggest a fix?
thank you!
class A{
public:
A(int val);
~A(){};
int val_a;
};
A::A(int val){
val_a = val;
};
class Foo {
public:
Foo();
~Foo();
void createA();
A* my_a;
};
Foo::Foo(){
createA();
};
void Foo::createA(){
my_a = new A(20);
};
Foo::~Foo(){
delete my_a;
};
class B {
public:
vector<Foo> vec;
void createFoo();
B(){};
~B(){};
};
void B::createFoo(){
vec.push_back(Foo());
};
int main(){
B b;
int i =0;
for (i = 0; i < 5; i ++){
std::cout<<"\n creating Foo";
b.createFoo();
std::cout<<"\n Foo created";
}
std::cout<<"\nDone with Foo creation";
std::cout << "\nPress RETURN to continue...";
std::cin.get();
return 0;
}
You need to implement a copy constructor and an assignment operator for Foo. Whenever you find you need a destructor, you alnmost certainly need these two as well. They are used in many places, specifically for putting objects into Standard Library containers.
The copy constructor should look like this:
Foo :: Foo( const Foo & f ) : my_a( new A( * f.my_a ) ) {
}
and the assignment operator:
Foo & Foo :: operator=( const Foo & f ) {
delete my_a;
my_a = new A( * f.my_a );
return * this;
}
Or better still, don't create the A instance in the Foo class dynamically:
class Foo {
public:
Foo();
~Foo();
void createA();
A my_a;
};
Foo::Foo() : my_a( 20 ) {
};
If you don't specify a copy constructor, the compiler makes one for you. Your compiler-generated copy constructor looks like this:
Foo::Foo(const Foo& copy)
: my_a(copy.my_a)
{}
Woops! You're copying just the pointer but not the pointed-to memory. Both your temporary Foo() in createFoo() and the one copied in to the vector point to the same memory, so the memory is deleted twice, which crashes your program on the second delete.
You should create a copy constructor that looks something like this:
Foo::Foo(const Foo& copy)
: my_a(new A(*copy.my_a))
{}
Note this crashes if copy has a NULL my_a member, and it also invokes the copy constructor on A, which you haven't specified either. So you'll want to make some further changes. You'll also want an operator= overload too.
The Foo object is copied and at the destruction of every copy, delete is called on the same pointer value my_a.
Implement the copy and assignment operator for Foo or use a smart pointer.
Foo( const Foo& s) : my_a( s.my_a ? new A(*s.my_a) : 0) {
}
Foo& operator= (const Foo& s) {
Foo temp(s);
temp.swap (*this);
return *this;
}
void swap (Foo &s) {
std::swap (my_a, s.my_a);
};