-3

This is simple program that is supposed to handle a dynamic array of numbers and then to filter out the elements that are even and put them into a new array and then print both arrays on the screen.

This is the header file:

#pragma once


namespace filter
{
    class Array
    {
        double *arr;
        int n;

public:
    Array();
    Array(int);
    Array(const Array&);
    Array(Array&&);
    void PutIn();
    void PrintOut() const;
    Array isEven();
    Array filter(const std::function<bool(int)>&) const;
    ~Array();

};
}

Then, this is the implementation of functions:

#include <iostream>
#include <functional>
#include "Array.h";
using namespace filter;
using namespace std;

Array::Array() :arr(nullptr), n(0)
{ }

Array::Array(int n)
{
    this->n = n;
    arr = new double[n];
}

Array::Array(const Array &a1)
{
    n = a1.n;
    arr = new double[n];
    for (int i = 0; i < n; i++)
        arr[i] = a1.arr[i];
}

Array::Array(Array &&a1)
{
    n = a1.n;
    for (int i = 0; i < n; i++)
        arr[i] = a1.arr[i];
    a1.n = 0;
    a1.arr = nullptr;
}

void Array::PutIn()
{
    cout << "Insert elements:\n";
        for (int i = 0; i < n; i++)
            cin >> arr[i];

}
void Array::PrintOut() const
{
    cout << "\nYour array is :\n";
    for (int i = 0; i < n; i++)
        cout << arr[i] << "\t";
}

Array Array::isEven()
{
    return filter([](int x) { return x % 2; });
}
Array Array::filter(const std::function<bool(int)> &f) const
{
    int b = 0;

    for (int i = 0; i < n; i++)
        if (f(arr[i]) == 0)
            b++;

    Array temp(b);
    b = 0;
    for (int i = 0; i < n; i++)
        if (f(arr[i]) == 0)
        {
            temp.arr[b] = arr[i];
            b++;
        }
    return temp;
}

Array::~Array()
{
    delete[]arr;
    n = 0;
}

Finally, this is the source code:

#include <iostream>
#include <functional>
#include "Array.h"
using namespace filter;
using namespace std;




int main()
{

    Array a(5);

    a.PutIn();

    Array b = a.isEven();    //WHY THIS LINE OF CODE INVOKES MOVE CONSTRUCTOR AND NOT COPY CONSTRUCTOR?

    a.PrintOut();
    b.PrintOut();

    getchar();
    getchar();

}

So, as you can see, this is relatively simple program that needs to handle an array with five elements in it entered by user and then to create a new array that consists of even elements of the first array. When i run this, it works fine, however, there is one little thing that i don't understand here.

If you look at the source code, notice the line where i left my comment, that is the line where move constructor is called, but i don't know why. That would mean that a.IsEven() is a RVALUE, since move constructor works with RVALUES, right? Can anyone explain me why this is rvalue and what is the correct way to understand this? Any help appreciated!

cdummie
  • 163
  • 1
  • 6
  • 2
    `isEven` returns an `Array` by value, which is a temporary, so it certainly would invoke a move construction of `b` instead of a copy. – Cory Kramer Nov 14 '18 at 19:14
  • 4
    What sense does it make to have the move constructor allocate a new array? The point is that, given that the source object can be left "empty", you can steal his pointer... Currently your implementation copies and then leaks the source object memory. – Matteo Italia Nov 14 '18 at 19:17
  • 2
    According ot the [rule of 3/5/0](https://en.cppreference.com/w/cpp/language/rule_of_three) you will need an assignment operator. Note that your move constructor not only doesn't move, it also leaks. – François Andrieux Nov 14 '18 at 19:18
  • 1
    i dont think this code is "relatively simple". Actually I think this is relatively complicated. Manual memory managment is never simple. Just saying... – 463035818_is_not_an_ai Nov 14 '18 at 19:21
  • 1
    unless this is one of those "dont do it the c++ way" assignment, you shuold use a `std::array` – 463035818_is_not_an_ai Nov 14 '18 at 19:25
  • @MatteoItalia Can you explain me your comment a bit, how am i supposed to steal an objects pointer? How this leaks source object memory? I see that i messed up this move constructor badly, i thought i just needed to use source object and then delete it, it just now pops-up in my mind that i have a destructor for it? Anyway, how am i supposed to do this properly and why this invoked move and not copy constructor? – cdummie Nov 14 '18 at 19:30
  • this might help: https://stackoverflow.com/questions/9456910/how-to-define-a-move-constructor – 463035818_is_not_an_ai Nov 14 '18 at 19:30
  • @user463035818 That's nice, but i am not supposed to use std::move here. – cdummie Nov 14 '18 at 19:32
  • 1
    yes I also realized that the question (and the answers) are rather specific. Dont find a better question and I dont want to point you to a random online ressource, try [here](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) maybe – 463035818_is_not_an_ai Nov 14 '18 at 19:34
  • @CoryKramer so, it means that every time i have a function that returns something by value and then i am assigning it to something else, move constructor would be called, right? – cdummie Nov 14 '18 at 19:35
  • @user463035818 i most certainly need to check out some of those books, since i am having trouble with basic concepts such as move semantics, thanks for the effort! – cdummie Nov 14 '18 at 19:38
  • @cdummie: you would just copy _the pointer_ and `n`, and then zero them out in `a1`. This way, you move the "guts" of `a1` in the newly constructed object, and leave an empty shell on the other side. That's the point of a move constructor: steal the stuff from the innards of the other object, leaving it in whatever state is more convenient, the only requirement is that its destructor will run cleanly. If you implement a move constructor that copies and then kill the other object, you could simply avoid to implement it, and leave it to the regular copy constructor. – Matteo Italia Nov 15 '18 at 12:52

1 Answers1

0

Your assumption that calling isEven invokes your move constructor is not in fact correct. Nor does it invoke your copy constructor. Instead, RVO ensures that the object returned is constructed directly at the calling site so neither is required.

Live Demo (which doesn't address any of the flaws in your code mentioned in the comments).

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • Are you suggesting that i don't even need move and copy constructor here? I don't think i understand. – cdummie Nov 14 '18 at 19:42
  • You might need them, but not to return `Array`s by value in the vast majority of cases. – Paul Sanders Nov 14 '18 at 19:44
  • I see, i thought it was invoked, since when i was debugging it, it appeared as if the move constructor was called. – cdummie Nov 14 '18 at 19:46
  • That's worth checking. My (simple) tests show that it is not - see live demo. – Paul Sanders Nov 14 '18 at 19:48
  • 1
    Not really - read the first paragraph of the 'RVO' (aka copy elision) link I provided - copy elision is an important optimisation when returning objects by value, I'm surprised the other commentors didn't mention it. – Paul Sanders Nov 14 '18 at 19:58
  • Ok, but still, when i tried debugging of this code in the visual studio it appeared as if move constructor was called ,this copy elision thing is something new for me. – cdummie Nov 16 '18 at 15:55
  • 1
    You're right, MSVC invokes the move constructor in the body of `isEven`. I would call this an MSVC bug - there's no need to do this. – Paul Sanders Nov 16 '18 at 18:24
  • Ok, i see, thanks for the help, im going to keep in mind that copy elision stuff, however, i suppose that at my level of knowledge, it is bit too advanced, since i am beginner in oop. – cdummie Nov 17 '18 at 15:16