30

So, I was just wondering how could we completely erase or reset a structure so it could be reused?

I just typed this up, here you go:

typedef struct PART_STRUCT
{
    unsigned int Id;
    std::string Label;
} Part;

typedef struct OBJECT_STRUCT
{
    std::vector< unsigned char > DBA;
    std::vector< Part > Parts;
    unsigned int Id;
} Object;

Object Engine;

// Initialize all members of Engine
// Do whatever with Engine
// ...
// Erase/Reset Engine value
user2117427
  • 467
  • 2
  • 6
  • 10
  • That typedef is an artefact from C, it doesn't do much here. Also just reset the members inside yourself or write a method to do it. – Rapptz Mar 03 '13 at 07:26
  • 1
    Jeez, why is everybody so trigger-happy with the downvotes? I +1 this back to 0. – Stephen Lin Mar 03 '13 at 07:30
  • 2
    The easiest solution is to write a method which does a reset of all the individual members. In C, we use `memset(&struct_var, 0, sizeof(struct whatever))` when we know for sure that 0 or NULL is an acceptable initial value for all of its members. But in C++, it is tough to keep this assumption true always. – Tuxdude Mar 03 '13 at 07:32
  • @Tuxdude disagree. The easiest solution is to write proper constructor. – Slava Mar 03 '13 at 07:39
  • 1
    @Slava - A constructor might not always be the solution to reset all the individual members if the constructor were intended to do something extra in addition to initializing the values. – Tuxdude Mar 03 '13 at 07:47
  • @Tuxdude I do not think having reset() method is any better, look into Rapptz answer, do you really think that code duplicate is good? – Slava Mar 03 '13 at 07:51
  • @Slava - I would rather have the constructor calling reset() in addition to whatever else it does rather than using the constructor for resetting. – Tuxdude Mar 03 '13 at 07:59
  • @Tuxdude that's bad design. – Rapptz Mar 03 '13 at 08:00
  • @Slava - care to explain why ? IMHO it is rather confusing and a poor design if someone uses the constructor for something that is not intended. – Tuxdude Mar 03 '13 at 08:01
  • @Tuxdude for what constructor is not intended? Constructor is intended to create fresh clean object, assign clean object to existing one is the same as resetting it, is it not? Where do you see unintended use of constructor? Though I am not saying that reset and reuse objects to save memory is good design. – Slava Mar 03 '13 at 08:22
  • 1) Field initializers are critical if class is inherited (or may be in the future). 2) Setting memory will cause corruption when a virtual member is added. – David V. Corbin Mar 14 '22 at 10:34

3 Answers3

57

You can just assign a constructed temporary to it:

Part my_struct;

my_struct = Part(); // reset

C++11:

my_struct = {}; // reset
Pubby
  • 51,882
  • 13
  • 139
  • 180
  • Hmm, that doesn't seem like a bad idea. – user2117427 Mar 03 '13 at 07:28
  • 2
    @user2117427 for that to work, you need proper default constructor in your structs – Slava Mar 03 '13 at 07:29
  • 1
    @Slava the compiler-generated one should work for structs. Certain members are going to be uninitialized though, which I think is UB, but I wouldn't worry about it in this case if you are too lazy to write a proper reset function. – Pubby Mar 03 '13 at 07:31
  • What does UB mean? :0 oh and +1 :) – user2117427 Mar 03 '13 at 07:32
  • @user2117427 undefined behavior. The bane of C++ programs. – Pubby Mar 03 '13 at 07:32
  • @Pubby: why do you need to write a proper reset function if you can write a proper constructor? – Slava Mar 03 '13 at 07:35
  • @user2117427 Actually, there is no UB. The ints will be reset to 0 in this case. – Pubby Mar 03 '13 at 07:36
  • It is wrong if ever the object needs more complicated destruction, for example freeing memory, closing files, etc. I would strongly suggest putting that in the answer so that nobody thinks it does the trick in any situation. – Qortex Mar 03 '13 at 07:42
  • @Mic As in every class, the `operator=` will also clean up the resources. If not, it's a problem with your class, not this code. – Pubby Mar 03 '13 at 07:44
  • Ok, in this case sure. But I'm not sure it is clear from the context above, might be worth mentioning it in the answer for anyone coming here through google. If the op had put `vector` with `new` allocated objects, then the answer would be modified, whereas it seems generic here. Just saying. – Qortex Mar 03 '13 at 07:52
  • @Mic if copy-ctor, assigment operator and dtor implemented properly (and they shoud anyway) there is no problem. – Slava Mar 03 '13 at 07:57
  • How would I apply this answer to a vector? Look at the codes in my question. Thanks :D – user2117427 Mar 03 '13 at 08:21
  • @user2117427 to reset a vector you can just call `clear`. Resetting `Engine` you can do what the answer says: `Engine = Object();` – Pubby Mar 03 '13 at 08:23
9

If for some reason I was hell-bent on keeping the same object constantly, I would just write a reset method that would reset all the values back to what they were.

Something similar to this:

struct Test {
    int a;
    int b;
    Test(): a(0), b(100) {}
    void reset() {
        a = 0;
        b = 100;
    }
};

int main() {
    Test test;
    //do stuff with test
    test.reset(); //reset test
}
Rapptz
  • 20,807
  • 5
  • 72
  • 86
  • @user2117427 and you should stop, if you do not want to generate bugs. At least call reset() from ctor, but better eliminate reset() method – Slava Mar 03 '13 at 07:53
  • @Rapptz this is error prone solution, change either ctor or reset to different value and forget its pair and boom. – Slava Mar 03 '13 at 07:55
  • @Slava not exactly advocating it truthfully, the premise of the question is sort of bad anyway. Reusing objects is a pretty weird thing to do IMO. – Rapptz Mar 03 '13 at 07:57
  • @Rapptz: if you call reset() from ctor it would be ok (just ok, not great, as Pubby provided clean solution), I would not -1. – Slava Mar 03 '13 at 07:59
  • I've switched to Pubby solution. Works fine. – user2117427 Mar 03 '13 at 08:00
  • @user2117427 Rapptz's solution works too, problem is not code itself. Bad habits die hard, having initialize() method was one of the solution in c++03 to avoid issues like this, and why constructor delegation is added to c++11. But initialize the same variables in 2 different places (or more) is very good way to get problems with support. – Slava Mar 03 '13 at 08:07
  • @Slava Can you please identify my bad habit? I would like to know so I can like stop? :0 – user2117427 Mar 03 '13 at 08:09
  • @user2117427 My concern is *why do you want to reuse objects*? – Rapptz Mar 03 '13 at 08:10
  • @user2117427 No. I guess I might be thinking wrong, usually when I imagine people reusing objects I imagine it being in the global scope or somewhere similar. – Rapptz Mar 03 '13 at 08:12
  • @user2117427 many people think: "this is a small program, I would write it shortest and ugly way, but when I write big enough program I will follow rules". Problem is following the model you make your habits, and change them later will not be easy at all. – Slava Mar 03 '13 at 08:12
  • @Slava I am actually trying to learn how to be more efficient - even if its a small program. I want to learn to be better at programming. However, I get really frustrated cause I know what I am doing isn't "correct". You know? So, it would be great if people point out my bad programming habits. – user2117427 Mar 03 '13 at 08:14
  • @user2117427 it does not matter how fast you program works, if it is incorrect. More important write readable, supportable code, you can easily make it unreadable aka optimized later, but vice versa is not easy at all. – Slava Mar 03 '13 at 08:18
  • @Slava "init" member functions are horrible and don't solve anything fyi – Cat Plus Plus Mar 03 '13 at 08:19
  • @Slava You know what? You are right. I guess it doesn't matter much about the speed atm. – user2117427 Mar 03 '13 at 08:19
  • @CatPlusPlus does copy-paste solve anything and better to duplicate initialization code instead? – Slava Mar 03 '13 at 08:29
  • 2
    @Slava Er what. You don't need init members to avoid code duplication. Adding init members create spurious object states that don't maintain invariants. It's funny that you'd advocate it and talk about code correctness at the same time. – Cat Plus Plus Mar 03 '13 at 08:34
  • 1
    @CatPlusPlus I do not advocate that, I said that such technique used in c++03 to avoid code duplicate (private init method that called from different ctors) and constructor delegate created in c++11 to avoid that. – Slava Mar 03 '13 at 08:38
  • If the OP actually wants a `reset()` method, I found it working to have one simply calling `*this = {};` to be working. My feelings tell me that there's something plain wrong and/or dangerous with this but I can't tell. My short tests show that at least this working method doesn't cause any compiler warning. – Roland Sarrazin Jan 29 '20 at 16:40
-1

Good practice is to avoid that type of construct (using the same variable for two different semantics meanings, having reset it in the meantime). It will inevitably create a weird bug later on when you (or somebody else) modify your code and forgets you shared a variable for two different uses.

The only justification would be to spare some memory space but:

  • It is very unlikely that you actually need such an optimisation.
  • Even if you do, the compiler will usually figure out a variable on the stack is no longer use and can be discarded, the new variable you would create will thus effectively replace the first one. You do not need to care about sparing the memory yourself.
  • If your variables are on the heap, you are better off just using two different pointers.

But if you really want to do this reset, you must write a method to do it. There is not built-in way in C++, because it would actually require calling the destructor and then the constructor again.

The solution my_struct = Part() works only if your destructor is trivial. Let's say you have allocated pointer in your std::vector, you would have to properly delete every pointer before emptying the vector. That's why it cannot be done automatically: the cleanup of the structure may require special treatment rather than plain forgetting.

Qortex
  • 7,087
  • 3
  • 42
  • 59
  • > The solution to just my_struct = Part() works only if your destructor is trivial. What? – Slava Mar 03 '13 at 07:41
  • sorry was a typo. Why -1? How do you think `boost::shared_ptr` does for example? You do not want to call a destructor yourself, so the only way I see is to write a `reset` method. – Qortex Mar 03 '13 at 07:44
  • my_struct = Part() works fine, and it works for boost::shared_pointer as well, so -1 cause it is not must, and reset only one of the alternative and I do not think the best one in this case. – Slava Mar 03 '13 at 07:47