Ошибка компилятора C ++: нет подходящей функции для вызова - PullRequest
2 голосов
/ 21 января 2010

Посмотрите на следующий код. Что с этим не так? Компилятор выдает эту ошибку:

В конструкторе копирования person::person(person&)': No matching function for call to person :: copy (char * &, char * &) ' кандидаты: void person :: copy (char * &, const char * &) "

Вот код:

class person
{
  public:
    person();
    person(person &);

  private:
    void copy(char*&,const char*&);
    char* name, *fathername,* address;
};

void person::copy( char*& n, const char*& p)
{
  int result;
  result=strcmp(n,p);
  if(result!=0)
  {
    n=new char[strlen(p)+1];
    strcpy(n,p);
    n[strlen(p)]='\0';
  }
}
person::person(person &object)
{
  copy(name,object.name);
  copy(fathername,object.fathername);
  copy(address, object.address);
}

Из ответов на этот Вопрос то, что я понял до сих пор, дано : компилятор не позволяет преобразовать ссылку в постоянную ссылку, потому что ссылки уже являются постоянными. Они не могут указывать на другую область памяти, такую ​​как указатель. Я прав?

Ответы [ 9 ]

10 голосов
/ 21 января 2010

Разве это не было бы лучше?

class person
{
private:
  std::string name;
  std::string fathername
  std::string address;
};

// constructor and copy constructor autogenerated!

Это скорее "C ++";).

7 голосов
/ 21 января 2010

Если вы не планируете менять указатели, вы не должны передавать ссылки на указатели:

Изменение:

void person::copy( char*& n, const char*& p) 

до

void person::copy( char* n, const char* p) 

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

Изменение, которое я предложил выше, допускает использование «указателя на const char» (p), таким образом предоставляя доступ только для чтения элементам через «p». Теперь «указатель на символ» разрешает доступ на чтение / запись к данным, поэтому преобразование этого значения в «указатель на константный символ» разрешено, поскольку мы просто ограничиваем разрешенное поведение.

Существует целый ряд других проблем с кодом, который вы разместили.
Вы хотите, чтобы мы перечислили их?

Я не делаю СЕЙЧАС. Я делаю в моем графике.

Проблемы:

1: при каждом вызове вы пропускаете копию:

if(result!=0)
{
    n=new char[strlen(p)+1];   // What happned to the old n?

2: используется оператор присваивания по умолчанию.

person a;
person b;
a = b; // a.name == b.name etc all point at the same memory location.
       // Though because you do not delete anything in the destructor
       // it is technically not an issue yet.

3: Вы удалили выделенные элементы в деструкторе.

{
     person  a;
} // A destructor called. You leak all the member here.

4: strcpy () уже копирует завершающий символ '\ 0'.

5: если вызов new вызывает исключение. Вы потеряете память.

copy(name,object.name); 
copy(fathername,object.fathername);   // If new throws in here.
                                      // Then the this.name will be leaked.

Правильно сделать это с помощью C-String настолько сложно, что даже у эксперта C ++ возникнут проблемы с этим. Вот почему эксперты C ++ будут использовать std :: string, а не C-String. Если вы должны использовать C-Strings, вам следует заключить C-String в другой класс, чтобы защитить его от исключений за исключением.

4 голосов
/ 21 января 2010

Изменение

человек :: человек (человек и объект)

до

человек :: человек (постоянный человек и объект)

для начинающих ...

3 голосов
/ 21 января 2010

Компилятор сообщает вам о проблеме - измените вашу подпись, чтобы она принимала 2 указателя char * (вместо 1 const char *), и он должен скомпилироваться.

Проблема действительно связана с использованием ссылки - если вы создали метод копирования, который просто взял 2 указателя char * (не ссылки), то компилятор автоматически распознает преобразование char * в const char * и использует метод. Поскольку у вас есть только метод, который принимает ссылку на другой тип, он не может сделать это автоматически.

1 голос
/ 22 июня 2011

Другие уже правильно ответили на ваш вопрос, но, похоже, вы до сих пор не поняли его, поэтому я постараюсь сделать его максимально понятным для вас.

void person::copy( char*& n, const char*& p)

Эта функция ожидает в качестве второго аргумента неконстантную ссылку на константный указатель (а не константную ссылку на указатель, как вы могли бы подумать!).

Когда вы пытаетесь вызвать эту функцию, передавая в качестве второго аргумента указатель (не указатель const), компилятор не может создать для него ссылку, просто потому, что он ожидает привязки ссылки к указателю const, а это не так. разрешено неявно приводить указатель к константному указателю, поскольку неконстантные ссылки могут не связываться с r-значениями (временными значениями).

Если вы хотите, чтобы функция ожидала константную ссылку на указатель константы, вы должны изменить ее сигнатуру, как показано ниже:

void person::copy( char*& n, const char* const& p)

Здесь важно понимать, что компилятор неявно преобразует предоставленный указатель в указатель const перед привязкой ссылки, что разрешено в этом случае, так как ссылка const может связывать как значения rvalue, так и значения l.

Аналогично, если вы хотите, чтобы функция ожидала константную ссылку на указатель, что, вероятно, было вашим первоначальным намерением, тогда подпись должна быть такой, как показано ниже:

void person::copy( char*& n, char* const& p)

Здесь компилятору не нужно ничего явно приводить, поскольку предоставленный аргумент уже соответствует типу, который ссылка ожидает связать.

Надеюсь, я дал вам ясное и подробное объяснение, и вы правильно поняли, что вызвало проблему, что действительно важно, но, тем не менее, я бы настоятельно советовал вам не писать свой код таким образом, а вместо этого следовать рекомендациям. дано другими.

1 голос
/ 22 января 2010

Это ОЧЕНЬ (!) Плохой дизайн. Этот код содержит ошибки и ОЧЕНЬ (!) Трудно понять и поддерживать. Этот вопрос является продолжением этого вопроса: C ++ классы, объектно-ориентированное программирование .

Теперь вы боретесь с симптомами, а не с реальной проблемой. И настоящая проблема заключается в том, чтобы думать в терминах C ++, а не в C (если вы хотите стать объектно-ориентированным программистом C ++).

Действительный код C ++ (C ++, а не C с классами) здесь:

#include <string>

class person
{
public:
  person();
private:
  std::string name, fathername, address;
};

Вот и все. Все остальные вещи (включая копирование contstructor) компилятор C ++ генерирует для вас (так же эффективно, как и ваша собственная реализация вручную)! Это намного проще, намного понятнее, проще в обслуживании и понимании, и в первую очередь: без ошибок;). И это настоящий код C ++.

1 голос
/ 22 января 2010

Как говорят другие, вы не должны передавать указатель на символ по ссылке, если не собираетесь его изменять.

Проблема в том, что ссылка неконстантна и поэтому не привязана к временным файлам. Поэтому тип передаваемой переменной должен точно совпадать. Близкие совпадения, в которых задействовано неявное приведение, неприемлемы, поскольку результат неявного преобразования является временным.

Ссылки Const, с другой стороны, могут быть привязаны к временным файлам.

void non_constant(int&);
void constant(const int&);

int main()
{
    int i = 0;
    unsigned u = 0;
    non_constant(i);
    //non_constant(u);  //ERROR: not an int
    //non_constant(10);  //ERROR: literals are temporaries
    constant(i);
    constant(u);  //OK, unsigned implicitly cast to int
    constant(10); //OK, literals bind to const references
}

Итак, если вы очень хотели сохранить ссылку в аргументе:

void person::copy( char*& n, const char* const& p)
1 голос
/ 22 января 2010

Я чувствую себя щедрым, так что исправленная версия вашего кода:

class person
{
public:
    person();
    person(const person &);
    ~person();
private:
    void copy(char*&,   // Do you understand purpose of '&' here?
              const char*);
    char* name;
    char* fathername;
    char* address;
};

person::person()
    : name(NULL),
      fathername(NULL),
      address(NULL)
{
}

person::~person()
{
    delete[] name;
    delete[] fathername;
    delete[] address;
}

void person::copy( char*& n,  // The '&' is required because the contents of `n` are changed.
                   const char* p)
{
    delete[] n;
    n = NULL;     // Here is one place where contents of `n` are changed.
    if (p)
    {
        n = new char [strlen(p) + sizeof('\0')];  // Another content changing location.
        strcpy(n, p);
        n[strlen(p)]='\0';
    }
}

person::person(const person& object)
{
    copy(name,object.name);
    copy(fathername,object.fathername);
    copy(address, object.address);
}

Можете ли вы определить недостатки или предметы безопасности, все еще скрывающиеся?

0 голосов
/ 23 января 2010

Другие указали, что вы должны заменить ссылку указателем.

Вот несколько других, но связанных комментариев:

  1. Если вы определяете конструктор копирования, также задайте оператор присваивания. В большинстве случаев они должны идти вместе.

  2. Рекомендуется объявлять конструктор с одним аргументом как explicit.

  3. Называть объекты как объекты - это плохое соглашение и может привести к путанице. Очевидно, что каждый экземпляр типа person является объектом. Используйте более значимые имена, такие как person(const person& other); или person(const person& rhs); // after right-hand-side

  4. Используйте std::string. Если вы программируете на C ++, нет рациональная причина не использовать std::string и вместо этого манипулировать C-строками.

  5. Наконец, позаботьтесь о безопасности исключений, следуйте передовым методам, таким как копирование операций, выполненных с точки зрения операции замены без выброса. См. Статью Исключительно безопасное проектирование класса, часть 1: присвоение копии Хербом Саттером

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...