Переменная инициализирована значением мусора в конструкторе копирования

В моем классе comp sci мы создаем векторный класс. Я застрял, потому что мой конструктор копирования инициализирует мою «предельную» переменную значением мусора, и я не понимаю, почему.

Вот мой основной:

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;

Вот конструктор, который принимает 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]);
   }
}

Вот конструктор копирования

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

Вот как выглядит резерв

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

Мое утверждение в main терпит неудачу, потому что я перегрузил оператор ==, чтобы сначала проверить, равны ли ограничения. Если это не так, функция возвращает false. После этого функция продолжается, но это утверждение не выполняется, потому что я получаю для моего v1 значение 4, что правильно, а затем мой v2 - это какое-то большое число, которое не имеет смысла.

Я попытался переписать свой конструктор копирования, чтобы напрямую инициализировать члены класса без использования резерва, но он по-прежнему не работает. Он все еще ограничивает значения мусора. В остальном работает нормально.

Вот что выводит main ...

сделать это здесь?

abc

abc

4 7500072

Ошибка утверждения: v1 == v2, файл myCodeTres.cpp, строка 58

Я чувствую, что перепробовал все, что мог, чтобы исправить это, но ничего не работает. Что-нибудь посоветуете мне?

Спасибо.

Обновление: это ошибка компилятора, которую я получаю, когда оставляю амперсанд.

В файле, включенном из myCodeTres.cpp: 20: myMemory.hpp: В экземпляре конструкции 'T * (T *, Args && ...) [с T = myString; Args = {myString *}] ': myStringVector.cpp: 25: 36: требуется отсюда myMemory.hpp: 61: 10: ошибка: нет соответствующей функции для вызова' myString :: myString (myString *) '61 | вернуть новый (p) T (std :: forward (args) ...); | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

** тогда есть какие-то странные ошибки, которые отображаются синим цветом на экране компилятора **

В файле, включенном из myCodeTres.cpp: 23: myString.cpp: 42: 1: примечание: кандидат: 'myString :: myString (myString &&)' 42 | myString :: myString (myString && move): strLength (strlen (move.stringVar)) // перемещаем конструктор для перемещения содержимого из | ^ ~~~~~~~ myString.cpp: 42: 31: примечание: неизвестное преобразование аргумента 1 из myString * в myString && '42 | myString :: myString (myString && move): strLength (strlen (move.stringVar)) // перемещаем конструктор для перемещения содержимого из | ~~~~~~~~~~~ ^ ~~~ myString.cpp: 32: 1: примечание: кандидат: 'myString :: myString (const myString &)' 32 | myString :: myString (const myString & strToCpy): strLength (strlen (strToCpy.stringVar)) // конструктор копирования, удаляющий старые элементы | ^ ~~~~~~~ myString.cpp: 32: 36: примечание: неизвестное преобразование аргумента 1 из myString * в const myString & '32 | myString :: myString (const myString & strToCpy): strLength (strlen (strToCpy.stringVar)) // конструктор копирования, удаляющий старые элементы | ~~~~~~~~~~~~~~~~ ^ ~~~~~~~ В файле, включенном из myCodeTres.cpp: 23: myString.cpp: 23: 1: примечание: кандидат: 'myString :: myString (const char *, std :: size_t) '23 | myString :: myString (const char * cPnt, std :: size_t size): strLength (size) // конструктор для получения размера и const ptr для char | ^ ~~~~~~~ myString.cpp: 23: 1: примечание: кандидат ожидает 2 аргумента, 1 предоставлен myString.cpp: 14: 1: примечание: кандидат: 'myString :: myString (const char *)' 14 | myString :: myString (const char * cPnt): strLength (strlen (cPnt)) // конструктор для инициализации заданной const ptr в char. | ^ ~~~~~~~ myString.cpp: 14: 32: примечание: неизвестное преобразование аргумента 1 из myString * в const char * 14 | myString :: myString (const char * cPnt): strLength (strlen (cPnt)) // конструктор для инициализации заданной const ptr в char. | ~~~~~~~~~~~~ ^ ~~~ myString.cpp: 8: 1: примечание: кандидат: 'myString :: myString ()' 8 | myString :: myString () // конструктор по умолчанию: инициализирует пустую строку. | ^ ~~~~~~~ myString.cpp: 8: 1: примечание: кандидат ожидает 0 аргументов, 1 предоставлен

обновление: определение всего класса

 #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 файл

#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;
}

форматирование немного не работает, потому что загрузка кода в stackoverflow утомительна, насколько это возможно.


person Community    schedule 28.11.2020    source источник
comment
Опубликуйте минимальный воспроизводимый пример.   -  person n. 1.8e9-where's-my-share m.    schedule 28.11.2020
comment
Думаю, у вас может быть другая проблема: в одном месте у вас for(int i = 0; i < limit-1; i++), а в другом for(int i = 0; i < limit; i++). Я не понимаю, почему вы вообще добавили 1 к limit; кажется, это не служит другой цели, кроме как призыв к ошибкам. Также остается загадкой, какова цель last.   -  person David K    schedule 28.11.2020


Ответы (1)


Простая ошибка в вашем конструкторе копирования

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

должно быть

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

ИЗМЕНИТЬ

Это из конструктора списка инициализаторов работает.

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

А теперь хорошенько подумайте о типах здесь. Первый аргумент достаточно простой, это указатель на myString, т.е. myString*. Второй вариант немного сложнее, потому что вам нужно понимать std::initializer_list, но если вы проследите его, это будет константная ссылка на myString, то есть const myString&. Теперь это нормально, потому что construct определяется так

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

т.е. для некоторого типа T его первый аргумент является указателем на этот тип, а второй аргумент - это константная ссылка на этот тип.

Теперь посмотрим на типы для второго использования construct (тот, который не компилируется)

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

Первый аргумент - myString*, второй - const myString*. Два указателя! Вот почему он не компилируется! Ваше решение заключалось в том, чтобы добавить дополнительный указатель к первому аргументу. Типы

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

равны myString** и const myString*. Это компилируется, потому что у вас есть двойной указатель слева и один указатель справа, но это означает, что вы создаете указатели на myString, а не на объекты myString, и это не то, что вам нужно.

Вам следовало удалить указатель из второго аргумента, а не добавлять дополнительный указатель к первому аргументу. Другими словами, правильный код - это

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

Теперь типы myString* для первого аргумента и const myString& для второго аргумента. Точно так же, как в другом конструкторе.

Попробуйте это изменение и дайте мне знать, как оно пойдет.

person john    schedule 28.11.2020
comment
Хороший улов, хотя это было должно быть исправлено к настоящему времени. - person dxiv; 28.11.2020
comment
@dxiv Мы надеемся, что урок усвоен. - person john; 28.11.2020
comment
@dxiv Это не позволит мне не иметь амперсанда. Это еще одна проблема. Он действует так, как будто я передаю ему обычную строку myString, когда у меня нет амперсанда. И проблема даже не в конструкции. Это не инициализирует правильно LIMIT, который находится в резерве. Это то, о чем я спрашивал. - person ; 28.11.2020
comment
@dxiv Как видите, у него нет проблем с построением вектора с правильными значениями, так как выходные данные v1 и v2 - это a b и c. Проблема в ограничении. Пожалуйста, еще раз оцените мой вопрос с учетом лимита, я до сих пор не понял, почему он не инициализирует лимит правильно. - person ; 28.11.2020
comment
@JesseWood Какую ошибку вы видите при использовании амперсанда? Как видно из опубликованного вами кода, амперсанд в другом конструкторе, который вы написали, вам не нужен. А амперсанд неверный. Похоже, здесь есть какая-то проблема, которая не очевидна из опубликованного вами кода. - person john; 28.11.2020
comment
@john да, я считаю, что существует большая основная проблема, потому что компилятор говорит мне, что я не могу использовать myString в качестве аргумента, когда я оставляю амперсанд. Но, как вы сказали, в другом конструкторе конструкция принимает сначала без амперсанда, без проблем. Есть идеи, почему поведение так изменилось? - person ; 28.11.2020
comment
@JesseWood Укажите точную ошибку, которую вы получаете. Формулировка может дать ключ к разгадке проблемы, который вам не очевиден. К тому же, я думаю, нам нужно увидеть определение класса целиком, пожалуйста, включите это в вопрос. - person john; 28.11.2020
comment
@john обновлен! Спасибо за помощь. Я действительно потерялся. - person ; 28.11.2020
comment
@john, извините, я изначально забыл определение класса. Я добавляю это сейчас! - person ; 28.11.2020
comment
Итак, я думаю, что у меня есть объяснение проблемы с амперсандом. Это предположение, потому что я не вижу определения construct. И это может быть ваша настоящая проблема, а может и нет, но я думаю, вам стоит попробовать исправление, которое я предлагаю, и посмотреть, как обстоят дела. Это довольно долго, поэтому я собираюсь отредактировать его в своем ответе выше. Быть в курсе. - person john; 28.11.2020
comment
@JesseWood ОК, ответ обновлен. Я чувствую себя довольно уверенно, потому что думаю, что вы устанавливаете limit, но затем неправильный вызов construct, который я сейчас исправил, приводит к потере значения limit, и это объясняет результаты, которые вы видите. - person john; 28.11.2020
comment
@ Джон, большое тебе спасибо. Я сейчас на работе, но когда вернусь домой, я попробую ваш код и обновлю вас. - person ; 29.11.2020
comment
@john Замечательное объяснение! Сработало отлично. - person ; 29.11.2020
comment
@JesseWood Рад, что это работает. После того, как я написал это, я понял, что мое объяснение не совсем правильное. Определение construct, которое я использовал, устарело. construct изменилось в C ++ 11, но определение, которое я использовал, применимо к тому, как вы его используете, поэтому объяснение остается в силе. - person john; 29.11.2020