0

I know that std::vector::push_back() puts a copy in the vector. I am looking for a way or a std container which does not destroy the class members of an instantiated class.

I have a class, WindowsWindow with members,

private:
    GLFWwindow* m_pGLFWwindow = nullptr;
    EventHandler m_eventHandler;
    VertexArrayObject m_VAO;
    bool m_windowAvailable = true;
    std::uint8_t m_windowNumber = 0;
    std::string m_windowTitle = "";

Two of those members, viz., EventHandler, VertexArrayObject, are classes which themselves contain members which are classes.

When I std::vector::push_back() an instantiation of a WindowsWindow object into or onto a std::vector, all of the class member variables are destroyed since a copy is being made and then destroyed. Maybe the answer is to make a deep copy constructor for each class, which I have not tried.

I also tried,

std::vector<std::shared_ptr<WindowsWindow>> WindowsWindowVector;
std::shared_ptr<WindowsWindow> pmainWindow = std::make_shared<WindowsWindow>("Main Window", false);
...
WindowsWindowVector.push_back(pmainWindow);

Which fared no better.

  1. Is there a std container I might employ which does not make, store and destroy copies of the elements being added?

  2. Would a deep copy constructor for each class in question solve the problem?

  3. Would a standard "C" style array work?

I have gotten into the habit of using std::vector in place of the "C" style array for non-class objects so this is all a new experience.

user34299
  • 377
  • 2
  • 11
  • 2
    How about using a vector of pointers? – Paul Sanders Jan 03 '20 at 14:24
  • 2
    Using `std::vector< std::shared_ptr >` should get you the behavior you want. The `std::shared_ptr` objects may get copied from time to time, but the `WindowsWindow` objects they point to will not. – Jeremy Friesner Jan 03 '20 at 14:25
  • 2
    Sounds like the issue is you have not followed the [rule of three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) for the `WindowsWindow` class. – NathanOliver Jan 03 '20 at 14:25
  • 1
    push_back is making the copy of object being inserted using its copy constructor. If there is no custom one (like in case of your class), the default is just to copy all fields, using their own copy constructors, etc. If you want to make a copy that gets pushed into the vector, create a copy constructor. If you want to add the element into the vector without making a copy, create a vector of pointers instead. – Galan Lucian Jan 03 '20 at 14:26
  • 1
    If `WindowsWindow` follows [the rule of three](https://en.cppreference.com/w/cpp/language/rule_of_three) (e.g. has move ctor & assignment implemented, copy ctor & assignment disabled), the first option should've worked. Even if it doesn't, the second one should've worked. It's hard to say anything specific without a [mcve]. – HolyBlackCat Jan 03 '20 at 14:26
  • 1
    Btw if you want the compiler to ensure that your `WindowsWindow` class never gets copied (i.e. make it a compile-time error if any code tries to copy a `WindowsWindow` object), you can do so: https://stackoverflow.com/a/31940966/131930 – Jeremy Friesner Jan 03 '20 at 14:27
  • 2
    *I am looking for a way or a std container which does not destroy the class members of an instantiated class.* -- In other words, you're avoiding all copying and assignment because your class has incorrect copy semantics. It has nothing to do with `vector`, since copying and assigning can happen just by applying `=`, or passing / returning `WindowsWindow` by value. As the previous comment noted, if you want to make sure copies are not done, then turn off copying at compile-time, so that you can catch any entity making a copy of your object. – PaulMcKenzie Jan 03 '20 at 14:28
  • Have you considered using a list? The performance is almost universally inferior, but you won't have to worry about extra copies and deletes. That combined with `emplace_front` and `emplace_back` should eliminate all the extra copies you're trying to avoid. But yeah, you really should fix your copy/assignment code, or forbid it. – Mark Storer Jan 03 '20 at 14:35
  • 1
    Moveable stuff in a vector is almost always better than a list – Lightness Races in Orbit Jan 03 '20 at 15:02
  • @MarkStorer suggesting a list over fixing the rule of three is a recipe for just more problem in the long run. – Rosme Jan 03 '20 at 15:04
  • Agreed, but it does in fact address the question as asked. I strongly recommend that @user34299 either fix their Rule of Three issues, or mark copy/assign as deleted, as it'll save quite a bit of screaming, hair pulling, and/or self-flagellation in the long run. – Mark Storer Jan 03 '20 at 19:31

1 Answers1

4

You have two problems here.

  1. Your class is not copy-safe.

    This is a problem anyway. If you do not want your classes to have deep copy semantics, then that's fine they don't have to… but then it should be made safe to copy (by not owning & destroying the resources they refer to), or at least should be made non-copyable.

    Alternatively, as you say, make them deep-copyable (though this may not be what you want).

  2. You're copying unnecessarily.

    push_back doesn't have to copy. There is a push_back(T&&).

    So, consider std::moveing your objects into the vector instead. With a properly implemented move constructor and move assignment operator in WindowsWindow, this will be very cheap.

    You could also emplace-construct the element directly.

Neither of these is std::vector's fault, and creating a replacement vector type that does not do this would only be a strange hack around your class's broken copy/move semantics.


tl;dr: properly implement copy and move semantics for your class.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    And you'll get a bunch of move constructor and destructor calls if your vector needs to grow past its current reserve. – Mark Storer Jan 03 '20 at 14:32
  • I understand how to make a deep copy constructor. But how does one make a class “safe to copy” or “non-copyable”? I originally learned C++ in the early 2000s and then was away from it for many years. – user34299 Jan 03 '20 at 17:12
  • I tried using `::push_back(T&&)` with `std::move`, but the class objects within the class being added to the vector are still being destroyed. Is there another consideration of which I am unaware? – user34299 Jan 03 '20 at 17:13
  • @user34299 They will always be destroyed. But you have moved your resource-ownership and data from them, right? So it doesn't matter. You're destroying dead husks. I suggest reviewing the chapter in your book about object lifetimes and move semantics ;) – Lightness Races in Orbit Jan 03 '20 at 17:18
  • I have added ` private: WindowsWindow(const WindowsWindow& other) = delete; WindowsWindow& operator=(const WindowsWindow& other) = delete; ` to the WindowsWindow class with no definitions. I have defined `std::vector WindowsWindowVector;`. Finally, I added ` WindowsWindowVector.push_back(std::move(mainWindow));`. The Visual Studio 2019 compiler generates error C2248, viz., `WindowsWindow::WindowsWindow`: cannot access private member declared in class `WindowsWindow`. I understood `push_back()` does not have to copy, and doesn't, if used with `std::move()`. – user34299 Jan 05 '20 at 21:58