-1

Below is a very simple driver code for stack implementation I wrote:

#include <iostream>
#include <string>
#include "stack.h"
#include "vector.h"
 
void print(stack<std::string, vector<std::string>> stk)
{}
 
int main()
{
    vector<std::string>
        v1{"1","2","3","4"},
        v2{"Ɐ","B","Ɔ","D","Ǝ"};
 
    stack<std::string, vector<std::string>> s1{std::move(v1)};
    stack<std::string, vector<std::string>> s2{std::move(v2)};
 
    print(s1);
    print(s1);
    print(s2);
}

For some very weird reason, it crashes at runtime complaining about segmentation fault. For even more strange reason, the following works (and so does a few other orderings of print(sx) statements:

#include <iostream>
#include <string>
#include "stack.h"
#include "vector.h"
 
void print(stack<std::string, vector<std::string>> stk)
{}
 
int main()
{
    vector<std::string>
        v1{"1","2","3","4"},
        v2{"Ɐ","B","Ɔ","D","Ǝ"};
 
    stack<std::string, vector<std::string>> s1{std::move(v1)};
    stack<std::string, vector<std::string>> s2{std::move(v2)};
 
    print(s1);
    //print(s1);
    print(s2);
}

In case, you are wondering, why am I using vector instead of std::vector; I wrote my own vector class too. Thanks to Sam Varshavchik comment, the problem might be in the vector class. This is the link to it: vector

My machine specifications:

  • Ubuntu 22.04 LTS
  • GCC 11.2.0
  • -std = c++11
  • Compiled with g++ main.cc -o main.out (nothing fancy)

stack.h

#ifndef STACK_H
#define STACK_H

#include <utility>
#include <deque>

template<typename T, class Sequence = std::deque<T>>
class stack
{
public:
    typedef          Sequence                   container_type;
    typedef typename Sequence::value_type       value_type;
    typedef typename Sequence::reference        reference;
    typedef typename Sequence::const_reference  const_reference;
    typedef typename Sequence::size_type        size_type;

    stack() : c(Sequence()) {}
    explicit stack(const Sequence& __c) : c(__c) {}
    explicit stack(Sequence&& __c) : c(std::move(__c)) {}
    stack(const stack& __s) : c(__s.c) {}
    stack(stack&& __s) : c(std::move(__s.c)) {}
    ~stack() {}

    stack& operator=(const stack& __s)
    {
        c = __s.c;
        return *this;
    }
    stack& operator=(stack&& __s)
    {
        c = std::move(__s.c);
        return *this;
    }

    bool empty() const { return c.empty(); }
    size_type size() const { return c.size(); }
    reference top() { return c.back(); }
    const_reference top() const { return c.back(); }
    void push(const value_type& __x) { c.push_back(__x); }
    void push(value_type&& __x) { c.push_back(std::move(__x)); }
    void pop() { c.pop_back(); }
    void swap(stack& __s) { std::swap(c, __s.c); }

    inline bool operator==(const stack& __s) { return c == __s.c; }
    inline bool operator!=(const stack& __s) { return c != __s.c; }
    inline bool operator<(const stack& __s) { return c < __s.c; }
    inline bool operator<=(const stack& __s) { return c <= __s.c; }
    inline bool operator>(const stack& __s) { return c > __s.c; }
    inline bool operator>=(const stack& __s) { return c >= __s.c; }

protected:
    Sequence c;
};

#endif
  • 1
    Just because your vector class is believed to be "battle tested" -- that's insufficient for an exemption from Stackoverflow's [mre] requirements. After search/replacing `vector` with `std::vector` the shown code compiles and runs fine. valgrind shows no memory errors. A casual browse did not reveal any of the usual reasons for the described symptoms of memory corruption. The bug, if any, must be in the "battle tested" vector class. Unable to reproduce your memory corruption. – Sam Varshavchik Aug 04 '22 at 00:12
  • I apologize for using the word "battle tested", I meant that I tested it a lot many ways and It works as expected. How am I supposed to share the 500 lines of that "battle tested" vector class? I really would appreciate any help. – akaAbdullahMateen Aug 04 '22 at 00:16
  • I don't know what to tell you. The amount of shown code pretty much is already somewhat over the limit of what's a [mre] is. Clearly, adding that much more is not going to work, and noone will want to verify and then debug it themselves. Now, it is very simple for you to simply reproduce my results: replace `vector` with `std::vector` in the shown code. Add `#include `. It will compile, link, and run without crashing. If that's the case then you just proved to yourself the same, the bug must be somewhere else. – Sam Varshavchik Aug 04 '22 at 00:20
  • 1
    Have you run your code through valgrind? Built with address sanitizer turned on? – Stephen Newell Aug 04 '22 at 00:20
  • @StephenNewell nope! I am still a college student, and have no experience working with such tools. – akaAbdullahMateen Aug 04 '22 at 00:21
  • 2
    Then this is a great time to learn. Either will probably pinpoint your memory issue pretty much immediately. – Stephen Newell Aug 04 '22 at 00:21
  • Thanks for the advice! Will download it and let you know what result it produces. – akaAbdullahMateen Aug 04 '22 at 00:22
  • OK. **Update** I downloaded `valgrind` and ran it: showed 15 errors. XD guess my `vector` class really is not battle tested. – akaAbdullahMateen Aug 04 '22 at 00:34
  • Right, and this is easily seen with a naked eye. Vector's copy and move constructors are completely broken, and are `delete[]`ing an uninitialized pointer, as the first order of business. There are also a few other, non-fatal issues as well. I expect that the first error from valgrind will be shouting out an uninitialized memory read, followed by deleting something that's never newed. – Sam Varshavchik Aug 04 '22 at 00:35
  • @akaAbdullahMateen -- Your `vector` and `stack` class uses double underscores as a prefix in the variable names. Usage of double underscores is reserved for the compiler implementation. Thus unless you change those identifier names, such as `__base` to something else, that entire `vector/stack` class is ill-formed. You also should really put your `vector` and `stack` classes in their own namespaces, i.e. `myutils::vector` instead of `vector`. – PaulMcKenzie Aug 04 '22 at 03:35
  • @SamVarshavchik First of all, I want to apologize for such a late reply, I was asleep 15 min after posting this question. Second, yes, it is shouting about about deleteing something that is never newed. I am working on fixing these. This is my first time with valgrind, so the error messages ara a little hard to understand. Nevertheless, I would prefer to try to solve the issues on my own now. If I could not, I will inform in the qestion with an update. Second. Thanks so much for the all the useful points you mentioned. Have a nice day:) – akaAbdullahMateen Aug 04 '22 at 09:03
  • @PaulMcKenzie Thanks for pointing out the underscore issue. I am aware of the fact, that programs with double underscores in start are reserved for compiler. But that is the very reason, my program has it. While writing these classes, I was taking reference from **libstdc++** implementation, and seeing what they do. As a result, I wrote every class private member property, and class method parameter names with underscores just like they do. Guess it was not a wise decision. I will remove these underscores once I am finished with the problems **Sam Varshavchik** pointed. Thank you once again! – akaAbdullahMateen Aug 04 '22 at 09:08
  • @PaulMcKenzie Also one more thing. I do not enclose my classes in namespace (although I should, as you pointed out) because I fear some of the free functions (such as relational operators I define outside of the class for that class might not get called without namespace. Because they will be inside namespace but outside class, so I do not understand what will be the syntax when to use them? `myutils::somevector.operator<(othervector)` or `somevector myutils::< othervector` or simply `somevector < othervector`. – akaAbdullahMateen Aug 04 '22 at 09:13
  • 1
    @akaAbdullahMateen : [ADL](https://stackoverflow.com/q/8111677/636019) will make that a non-issue, so simply `somevector < othervector`. – ildjarn Aug 05 '22 at 10:31

1 Answers1

1

Your vector class defines these two constructors which are very sketchy:

vector(const vector& x) : __size(x.__size), __capacity(x.__capacity)
{
    delete [] __base;
    __base = new value_type[__capacity];
    for(size_type __c = 0; __c != __size; ++__c)
        __base[__c] = x.__base[__c];
}

vector(std::initializer_list<value_type> il) : __size(il.size()), __capacity(__size << 1)
{
    delete [] __base;
    __base = new value_type[__capacity];
    typename std::initializer_list<value_type>::iterator itr = il.begin();
    typename std::initializer_list<value_type>::iterator end = il.end();
    for(size_type __c = 0; __c != __size && itr != end; ++__c, ++itr)
        __base[__c] = *itr;
}

Why are they sketchy? Glad you asked! The member vector::__base is uninitialized at the point where you call the following:

delete [] __base;

Because __base has no defined value at this point, the behavior of deleting it is undefined. If it was NULL, this wouldn't be an issue.

Now, there really is no good reason to delete a member in your constructor. It will never have a value other than what you initialize it with. My guess is that you copy/pasted the code from your assignment operator, thus creating a serious bug.

To fix, you have two main options:

  1. Remove the delete[] __base; statement
  2. Add __base(nullptr) to the initializer list

The way your class is currently written, you should probably go with #1. However, if you were to extract your duplicated code from the copy constructor and assignment operator into a single helper method, it might always use delete[] and so you would likely go with option #2.

Just a side-note here... There's also a potential problem with your assignment operator. It should be testing for self assignment, otherwise assigning a vector to itself will completely break it:

vector& operator=(const vector& x)
{
    if (&x == this) return *this;

    // etc...
}
paddy
  • 60,864
  • 6
  • 61
  • 103
  • 2
    `__base` -- I would say that the entire class is broken due to the usage of double underscores as a prefix in the variable names. – PaulMcKenzie Aug 04 '22 at 03:34
  • Thank you so much for these guidelines. These alone did not entirely remove the problem but it sure put me on the right track. I am now double checking everything in the vector class. I remove the `delete` statements from constructor. ( The reason, it was there was because, in the whole code I used the common pattern of deleting current `__base`, creating a new one, and populating it). I also added the self-assignemnt check. Also, one thing: I usually check for self assignment with `if(*this == x) return *this;`. Is that correct? or should I be checking for address like yours. Thank you – akaAbdullahMateen Aug 04 '22 at 08:59
  • 1
    No, that's not correct, because it's invoking the equality operator that tests one vector against another. For example two separate vectors with the same contents are considered equal. That's quite different from checking if two things refer to the same object. You need to test the pointers, because then you know it's the same actual object. – paddy Aug 04 '22 at 09:14