Warm tip: This article is reproduced from serverfault.com, please click

Variable initialized with garbage value in copy constructor

发布于 2020-11-28 06:54:03

In my comp sci class we are making a vector class. I am stuck because my copy constructor is initializing my 'limit' variable with a garbage value and I dont understand why.

Here is my main:

myStringVector v1 {"a", "b", "c"};
std::cout << "make it here?" << std::endl;
myStringVector v2 = v1;
std::cout << v1[0] << v1[1] << v1[2] << std::endl;
std::cout << v2[0] << v2[1] << v2[2] << std::endl;
assert(v1 == v2);
std::cout << "Passed copy ctor" << std::endl;

Here is the constructor that takes an initializer_list

myStringVector::myStringVector(std::initializer_list<myString> list){
   this->reserve(list.size());
   for(int i = 0; i < limit-1; i++){
     construct(first+i, list.begin()[i]);
   }
}

Here is the copy constructor

myStringVector::myStringVector(const myStringVector& data){
  reserve((data.limit)-1);
  for(int i = 0; i < data.limit-1; i++){
    construct(&first+i, data.first+i);
  }
}

Here is what reserve looks like

void myStringVector::reserve(int n){
  this->first = allocate<myString>(n);
  this->last = first+n;
  this->limit = n+1;
}

My assertion in main is failing because I overloaded the == operator to first check if the limits are equal. If they are not, the function returns false. The function continues after that, but it is failing the assertion because I am getting my v1 to be 4, which is correct, and then my v2 is some large number that doesn't make sense.

I've tried rewriting my copy constructor to directly initialize the members of the class without using reserve and it still doesn't work. It is still assigning garbage values to limit. The rest of it is working fine.

Here is what main is outputting...

make it here?

abc

abc

4 7500072

Assertion failed: v1 == v2, file myCodeTres.cpp, line 58

I feel like I've tried everything I can think of to try and fix this but nothings working. Any advice for me?

Thanks.

Update: these are the compiler error I get when I leave the ampersand out.

In file included from myCodeTres.cpp:20: myMemory.hpp: In instantiation of 'T* construct(T*, Args&& ...) [with T = myString; Args = {myString*}]': myStringVector.cpp:25:36: required from here myMemory.hpp:61:10: error: no matching function for call to 'myString::myString(myString*)' 61 | return new (p) T(std::forward(args)...); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**then there are some weird errors that are in blue on the compiler screen **

In file included from myCodeTres.cpp:23: myString.cpp:42:1: note: candidate: 'myString::myString(myString&&)' 42 | myString::myString(myString&& move) : strLength(strlen(move.stringVar)) //move constructor to move contents from | ^~~~~~~~ myString.cpp:42:31: note: no known conversion for argument 1 from 'myString*' to 'myString&&' 42 | myString::myString(myString&& move) : strLength(strlen(move.stringVar)) //move constructor to move contents from | ~~~~~~~~~~~^~~~ myString.cpp:32:1: note: candidate: 'myString::myString(const myString&)' 32 | myString::myString(const myString& strToCpy) : strLength(strlen(strToCpy.stringVar)) //copy constructor that removes old elements | ^~~~~~~~ myString.cpp:32:36: note: no known conversion for argument 1 from 'myString*' to 'const myString&' 32 | myString::myString(const myString& strToCpy) : strLength(strlen(strToCpy.stringVar)) //copy constructor that removes old elements | ~~~~~~~~~~~~~~~~^~~~~~~~ In file included from myCodeTres.cpp:23: myString.cpp:23:1: note: candidate: 'myString::myString(const char*, std::size_t)' 23 | myString::myString(const char* cPnt, std::size_t size) : strLength(size) //constructor to take a size and const ptr to char | ^~~~~~~~ myString.cpp:23:1: note: candidate expects 2 arguments, 1 provided myString.cpp:14:1: note: candidate: 'myString::myString(const char*)' 14 | myString::myString(const char* cPnt) : strLength(strlen(cPnt)) //constructor to initialize to a given const ptr to char. | ^~~~~~~~ myString.cpp:14:32: note: no known conversion for argument 1 from 'myString*' to 'const char*' 14 | myString::myString(const char* cPnt) : strLength(strlen(cPnt)) //constructor to initialize to a given const ptr to char. | ~~~~~~~~~~~~^~~~ myString.cpp:8:1: note: candidate: 'myString::myString()' 8 | myString::myString() //default constructor: initializes empty string. | ^~~~~~~~ myString.cpp:8:1: note: candidate expects 0 arguments, 1 provided

update: whole class definition

 #ifndef MYSTRINGVECTOR_HPP
 #define MYSTRINGVECTOR_HPP

  #include "myString.hpp"
  #include <initializer_list>

  class myStringVector{
  private:
  myString *first, *last;
  int limit = 0;
  public:
  void reserve(int); //allocate memory at 'first' for n myString's
  myStringVector(); //call to reserve(0)
  myStringVector(std::initializer_list<myString>);
  myStringVector(const myStringVector&);
  bool empty();
  bool operator==(const myStringVector&);
  myString operator[](int);
  int getSize() const;
};

#endif //MYSTRINGVECTOR_HPP_INCLUDED

.cpp file

#include "myStringVector.hpp"
#include "myMemory.hpp"
#include <initializer_list>

void myStringVector::reserve(int n){
  this->first = allocate<myString>(n);
  this->last = first+n;
  this->limit = n+1;
}

myStringVector::myStringVector(){
 this->reserve(0);
 } 

 myStringVector::myStringVector(std::initializer_list<myString> list){
 this->reserve(list.size());
 for(int i = 0; i < limit-1; i++){
  construct(first+i, list.begin()[i]);
 }
 }

myStringVector::myStringVector(const myStringVector& data){
 reserve((data.limit)-1);
  for(int i = 0; i < data.limit-1; i++){
    construct(first+i, data.first+i);
 }
}

bool myStringVector::empty(){
  return (limit-1 == 0);
}

 bool myStringVector::operator==(const myStringVector& data){
 std::cout << limit << " " << data.limit << std::endl;
  if(this->limit != data.limit)
   return 0;
  for(int i = 0; i < limit; i++){
    if(*(first+i) != *(data.first+i))
      return 0;
  }
 return 1;
}

myString myStringVector::operator[](int index){
  return *(first+index);
}

int myStringVector::getSize() const{
  return limit-1;
}

formatting is a bit off because uploading code to stackoverflow is tedious as can be.

Questioner
user12633830
Viewed
22
john 2020-11-29 04:06:47

Simple error, in your copy constructor

construct(&first+i, data.first+i);

should be

construct(first+i, data.first+i);

EDIT

This from the initializer list constructor works.

construct(first+i, list.begin()[i]);

Now think carefully about the types here. The first argument is simple enough, it's a pointer to myString i.e. myString*. The second is a little more complicated because you have to understand std::initializer_list but if you follow it through it's a const reference to myString, i.e. const myString&. Now this is fine because construct is defined like this

template <class T>
void construct(T* first, const T& second);

i.e. for some type T it's first argument is a pointer to that type, and it's second argument is a const reference to that type.

Now look at the types for the second use of construct (the one that doesn't compile)

construct(first+i, data.first+i);

The first argument is myString* and the second argument is const myString*. Two pointers! This is why it doesn't compile! Your solution was to add an extra pointer to the first argument. The types of

construct(&first+i, data.first+i);

are myString** and const myString*. That compiles because you have a double pointer on the left and a single pointer on the right but it means that you are constructing pointers to myString not myString objects, and that is not what you want.

What you should have done is remove the pointer from the second argument, not add an extra pointer to the first argument. In other words the correct code is this

construct(first+i, data.first[i]);

Now the types are myString* for the first and const myString& for the second argument. Exactly the same as in the other constructor.

Try that change and let me know how it goes.