4

I'm dealing with C++ objects that use destructor to deallocate resources. When passing those objects to a function with an arbitrary number of parameters. Is there any way to get rid of using pointers in this case?

One possible solution is passing a vector of pointers to those objects. if I pass the objects themselves, the destructors will be called twice, one is the real objects, another is when the vector is deallocated.

#include<iostream>
#include<vector>
class integer {
    private:
        int data;
    public:
        integer(int value = 0): data(value) {}
        ~integer() {}
        void increase() {
            data++;
        }
        friend std::ostream& operator<<(std::ostream& os, const integer& x);
};
std::ostream& operator<<(std::ostream& os, const integer& x) {
    os << x.data;
    return os;
}
void foo(std::vector<integer*> vec) {
    for (auto it = vec.begin(); it != vec.end(); it++) {
        (*it)->increase();
    }
}
int main() {
    integer x1(3);
    integer x2(4);
    integer x3(5);

    std::cout << x1 << " " << x2 << " " << x3 << std::endl;

    foo({&x1, &x2, &x3});

    std::cout << x1 << " " << x2 << " " << x3 << std::endl;

    return 0;
}

Expected results:

3 4 5
4 5 6
L. F.
  • 19,445
  • 8
  • 48
  • 82
khanh
  • 600
  • 5
  • 20
  • 1
    No, the destructor of `vector` calls the destructor of `integer*`, which is a no-op and does not call the destructor of `integer`. – L. F. May 30 '19 at 09:46
  • `the deconstructors will be called twice, one is the real objects, another is when the vector is deallocated.` That is not entirely true, because those objects will be copied, so there are two copies of each and both have to be deallocated. – Quimby May 30 '19 at 09:47
  • 1
    The code shown above matches the requirement. The actual question is how to get rid of using pointers. Thanks – khanh May 30 '19 at 09:47
  • Why would you use pointers in the first place? – Timo May 30 '19 at 09:48
  • 3
    Why? What's wrong with pointers? That's exactly the ownership you want. – Quimby May 30 '19 at 09:48
  • @Quimby Why not passing the vector as a reference: `void foo(std::vector vec)` to `void foo(std::vector& vec)`? – Timo May 30 '19 at 09:51
  • @Quimby Since C++ provides references, it might be a better approach to not to use pointers. – khanh May 30 '19 at 09:51
  • 1
    @Timo Because then it would not increment the integers `x1, x2, x3` in main. – Quimby May 30 '19 at 09:52
  • @Timo I've tested this, the compiler will create a vector of integer objects, that calls copy constructor and finally calls destructor. – khanh May 30 '19 at 09:53
  • 1
    Ha, joke's on me. Sorry about that. – Timo May 30 '19 at 09:53
  • 1
    @Timo It happens :) – Quimby May 30 '19 at 09:53
  • Possible duplicate of [Why can't I make a vector of references?](https://stackoverflow.com/questions/922360/why-cant-i-make-a-vector-of-references) – jaket May 31 '19 at 00:01
  • @jaket Oh, yes, It's duplicated. – khanh May 31 '19 at 02:55

2 Answers2

4

I'm not sure what is wrong with pointers, but of course you can avoid pointers with reference_wrappers. (Don't forget to #include <functional>)

void foo(const std::vector<std::reference_wrapper<integer>>& vec)
{
    for (auto it = vec.begin(); it != vec.end(); it++) {
        (*it)->increase();
    }
}

Now you can call it like foo({x1, x2, x3}).

live demo


You can even get rid of the {} if you want:

template <typename... Args>
void foo(Args&... args)
{
    static_assert(std::conjunction_v<std::is_same<Args, integer>...>);
    (args.increase(), ...);
}

(Don't forget to #include <type_traits>.) Now you can call it like foo(x1, x2, x3).

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • 1
    I've tried the live demo, does `std::reference_wrapper` work only on Clang compiler? gcc gives errors. – khanh May 30 '19 at 10:10
  • 1
    @NgọcKhánhNguyễn My bad! I forgot to `#include `. I have updated the demo. – L. F. May 30 '19 at 10:12
1

You can use iterators. Once you have more than 3 you should not continue with x4 but use a containter anyhow. Your foo would be

template <typename iterator_t>
void foo(iterator_t begin,iterator_t end) {
    for ( ; begin != end; ++begin) begin->increase();
}

And in main:

int main() {
    std::vector<integer> x { {3},{4},{5} };
    for (const auto& i : x) std::cout << i << " ";
    std::cout << "\n";

    foo(x.begin(),x.end());
    for (const auto& i : x) std::cout << i << " ";

    return 0;
}
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185