-1

I am a new user here and this is my first question, so please don't judge me hard. I cheked many similar questiontions like this not only on this site, also on others too. But I didn't find the answer.

The problem is to create copy constructor with pointers. For those people who may ask: why do you need pointers?: I have a class stmt, which contains vector<stmt>, it will allow me to construct tree-like data structure. Then I will perform some functions to change values of stmt's parameters. Without pointers they won't change

It compiles, but then gives Exception Unhandled (Runtime error)

My first attemp looks like this:

struct stmt
{
    string lexeme;
    int *size;
    int *lay;
    bool *nl;
    vector<stmt>* follow;
    stmt(string l)
    {
        lexeme = l;
        size = new int;
        *size = l.size()+2;
        lay = new int;
        *lay = 0;
        nl = new bool;
        *nl = false;
        follow = new vector<stmt>;
    }
    stmt(const stmt &s)
    {
        lexeme = s.lexeme;      
        size = new int;         //Crashes here : Unhandled exception:std::length_error at memory location ... 
        *size = *s.size;
        lay = new int;
        nl = new bool;
        follow = new vector<stmt>;
        follow = s.follow;
    }
};

Second time I tried also this:

stmt(const stmt &s)
:lexeme.(s.lexeme), size (s.size), ......and so on
{}

Unfortunately, this also doesn't help.

So, this is my third attemp but didn't help

IMPORTANT: I noticed that it happens when I am trying to create and emplace_back new stmt element in a vector of another stmt using functions which return type is stmt.

Here is a code which represents the key problem:

stmt Program("Program");
stmt ParseRelop(string  p);

void EMP(stmt s)
{
    s.follow->push_back(ParseRelop("token"));
}

stmt ParseRelop(string  p)
{
    stmt s(p);
    return s;
}

int main()
 {
    EMP(Program);
    cout<<Program.follow->at(0).lexeme;
 }
  • 4
    Do you *have* to use pointers? Foregoing the practice will give you a type that can be copied by the implicitly generated copy constructor. All your pointed-at members have value semantics. – StoryTeller - Unslander Monica May 22 '20 at 16:59
  • 1
    Avoid raw owning pointer, and use smart pointer instead (or value directly) or container. – Jarod42 May 22 '20 at 17:00
  • "It compiles, but then" - that it compiles just means that you wrote something that is syntactically valid - the compiler understood your instructions. It means next to nothing about whether your code is actually *correct* or free of bugs. – Jesper Juhl May 22 '20 at 17:01
  • @JesperJuhl They're probably aware of this, since they're here asking for help with the problem. Knowing that the problem is a _runtime_ error is valuable information that we want them to tell us; let's not berate them over it. – Asteroids With Wings May 22 '20 at 17:11
  • @Asteroid I'm not trying to "berate them". Just stating a fact that *many* people miss. – Jesper Juhl May 22 '20 at 17:18
  • @Jarod42 I can't use values directly, cause I will have a data structure with several layers and I perform many functions that will change their value. But I didn't know about container. Let's check! – Tattimbet Zakariya May 22 '20 at 17:22
  • @TattimbetZakariya *cause I will have a data structure with several layers and I perform many functions that will change their value* -- But the data structure you're showing us doesn't need to use any pointers -- you have simple `int`, `std::string`, `bool` and `std::vector`. None of that needs pointer usage. Why not wait until you have something that actually requires pointers, and then ask the questions when you reach that stage? – PaulMcKenzie May 22 '20 at 17:41
  • Your approach is runtime-inefficient, memory-inefficient, and exception-unsafe. Just use values, or show us that "multi-layer" case where you think pointers are necessary. – Daniel Langr May 22 '20 at 17:48
  • @DanielLangr Actually there is an example in the question body – Tattimbet Zakariya May 22 '20 at 17:56
  • @TattimbetZakariya -- The comment section is for comments on the code, so I made a comment. The problem with you trying this with simple variables is that it will *not* prepare you for the actual case you're claiming you are implementing. That's why we would like to see the actual case. – PaulMcKenzie May 22 '20 at 18:12
  • `void EMP(stmt s)` -- The `s` is a temporary variable. That's why you have no items in the vector when this function returns to `main`. You should be passing `s` by reference, not by value. That should be `void EMP(stmt& s)` – PaulMcKenzie May 22 '20 at 20:19

2 Answers2

1

Like this

stmt(const stmt &s)
{
    ...
    follow = new vector<stmt>(*s.follow);
}

This version allocates a new vector by copying the vector from the copied object (*s.follow is the vector in the copied object).

Your version allocated a new pointer but then overwrote it with a copy of the pointer from the copied object.

You want to copy the vector, not the pointer that's pointing at the vector.

You should use the same technique for your other pointers

    size = new int(*s.size);
    lay = new int(*s.lay);

etc.

john
  • 85,011
  • 4
  • 57
  • 81
  • Seems helpful, but I also have problem with other pointers: that I left a comment. What can I do with *size, could you help with that? – Tattimbet Zakariya May 22 '20 at 17:15
  • Sorry, doesn't helped. At least thank you for the answer – Tattimbet Zakariya May 22 '20 at 17:21
  • @TattimbetZakariya The code above is a correct way to copy pointers in a copy constructor. If your code is still not working then you probably have other bugs. – john May 22 '20 at 17:22
  • I know and I cheked other similar questions, so that's why I also attached an example of usage in a Function. Please check that too! – Tattimbet Zakariya May 22 '20 at 17:26
  • 4
    _What can I do with *size?_ Declare it as an `int`, rather than an `int *`. There's no point using a pointer for a primitive type. Same for `lay` and `nl`. In fact, there's no reason to use pointers here at all. – Paul Sanders May 22 '20 at 17:39
  • Note that making this (RAII-free) approach exception-safe (to avoid resource leaks) is quite painful. That's why it's much much better to avoid raw pointer. – Daniel Langr May 22 '20 at 17:41
  • @TattimbetZakariya Your first, second and third examples all have the same problem. But I fixed that problem in the code above, did you actually try it? – john May 22 '20 at 19:09
  • @john if you mean this one:size = new int(*s.size); lay = new int; nl = new bool(*s.nl); follow = new vector(*s.follow); YES, I tried it also – Tattimbet Zakariya May 22 '20 at 19:28
  • @TattimbetZakariya Well my advice would be to stick with it, because it's a lot closer to the correct version than any of the other attempts. This is the problem when you have multiple bugs, you cannot tell when you've fixed one bug, because the others remain. It's hard to know when you are making progress. – john May 22 '20 at 19:33
  • @TattimbetZakariya In general terms my advice would be to start again. This time don't make the mistake of writing too much untested code all at once. Write a little code, test it thouroughly, then write a little more. Don't get yourself in the situation where you have so many problems in your code that you don't know if you're coming or going. – john May 22 '20 at 19:35
0

Let's start by making your stmt class copy correct. Once you do that, then the issues will come from other parts of your code that do not involve stmt leaking memory, or accessing invalid memory:

#include <vector>
#include <string>
#include <algorithm>

struct stmt
{
    std::string lexeme;
    int *size;
    int *lay;
    bool *nl;
    std::vector<stmt>* follow;

    // Constructor.  Note the usage of the member initialization list
    stmt(std::string l) : lexeme(l), size(new int), lay(new int(0)), nl(new bool(false)),
        follow(new std::vector<stmt>())
    {
        *size = l.size() + 2;
    }

    // Copy constructor.  Note that we use the member initialization list, 
    // and copy all the members from s to the current object, not partial copies
    stmt(const stmt &s) : lexeme(s.lexeme), size(new int(*s.size)), nl(new bool(*s.nl)),
        lay(new int(*s.lay)), follow(new std::vector<stmt>(*s.follow))
    {}

    // The assignment operator.  Note that we use copy / swap to swap out
    // all the members, and that a check for self-assignment is done.
    stmt& operator=(const stmt &s)
    {
        if (this != &s)
        {
            stmt temp(s);
            std::swap(lexeme, temp.lexeme);
            std::swap(size, temp.size);
            std::swap(lay, temp.lay);
            std::swap(nl, temp.nl);
            std::swap(follow, temp.follow);
        }
        return *this;
    }

    // The destructor destroys all the memory 
    ~stmt()
    {
        delete size;
        delete lay;
        delete nl;
        delete follow;
    }
};

This class now follows the rule of 3 properly, and should be safely copyable. I put in the comments what each function does, and you should research on why the class should work correctly now.

I didn't want to go too much in depth, because in reality you wouldn't have pointers to int, bool or even vector<stmt>.

References:

Rule of 3

Copy / Swap Idiom

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
  • I copied the code that you wrote, but the problem is still remaining. Thank you for your try and advices. Could you please check the VERY CLEAR AND SHORT example that shows my exact error case on the bottom of question body and tell what do you think about that? – Tattimbet Zakariya May 22 '20 at 20:14
  • The error that you have as absolutely nothing to do with the code I posted. It has everything to do with you using a temporary variable here: `void EMP(stmt s)`. However, the answer I posted fixes an issue with that call, and that copying `stmt` objects is now safe, even though you used it incorrectly when you passed by value. – PaulMcKenzie May 22 '20 at 20:21
  • No it is not meaningless -- it points out the flaws of passing object by value, and expecting the results to reflect back to the caller. If you now make the change to `void EMP(stmt& s);` that small example will now work without issues. – PaulMcKenzie May 22 '20 at 20:24
  • I think you are the first who is getting right to the problem. Do you have any idea how to fix it? I would appreciate that! – Tattimbet Zakariya May 22 '20 at 20:27
  • Make sure your functions are taking references as parameters if you expect to see the results reflected in the caller. That one mistake with `EMP` -- make sure you're not making that same mistake in your actual program. Other than that, the `stmt` class, by itself, is no longer an issue. – PaulMcKenzie May 22 '20 at 20:29
  • Now I really understood my problem. Before I couldn't get why are you saying that pointers aren't nessecary (actually, everyone were trying to tell me that, my bad), I just didn't know that in function parameters we can just put `&` sign after a variable to refer to its pointer, however nobody expalined me about that feature. Thank you for your guide! – Tattimbet Zakariya May 22 '20 at 22:02