簡體   English   中英

C ++編譯器錯誤:沒有匹配的調用函數

[英]C++ Compiler Error: No matching function for call

請查看以下代碼。 這有什么問題? 編譯器給出了這個錯誤:

在復制構造函數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);
}

從這個問題的答案來看,到目前為止我所理解的是 :編譯器不允許將引用轉換為常量引用,因為引用已經是常量。 它們不能指向像指針一樣的不同內存位置。 我對嗎?

這不會更好嗎?

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

// constructor and copy constructor autogenerated!

這種方式更像是“C ++”;)。

除非您計划更改指針,否則不應將引用傳遞給指針:

更改:

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

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

這是因為p是對特定類型的引用。
您傳遞的對象不是確切的類型,因為它是一個參考,他們無法轉換它。

我在上面建議的改變允許“指向const char的指針”(p),從而允許通過'p'只讀訪問元素。 現在,“指向char的指針”允許對數據進行讀/寫訪問,因此允許將其轉換為“指向const char的指針”,因為我們只是限制了允許的行為。

您發布的代碼存在一系列其他問題。
你想讓我們列出它們嗎?

我現在不做。 我按照我的日程安排。

問題:

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包裝在另一個類中,以保護它免受異常的問題。

更改

person :: person(person&object)

person :: person(const person&object)

對於初學者...

編譯器告訴你問題 - 更改簽名以接受2個char *指針(而不是1個const char *)並且它應該編譯。

問題實際上是由於使用了引用 - 如果你創建了一個簡單地使用2個char *指針(而不是引用)的復制方法,那么編譯器將自動識別從char *到const char *的轉換並使用該方法。 由於您只有一個接受對不同類型的引用的方法,因此無法自動執行此操作。

我感覺很慷慨,所以這里是你的代碼的更正版本:

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

你能否識別潛伏的瑕疵或安全物品?

正如其他人所說,如果你不打算修改它,你不應該通過引用傳遞char指針。

問題是引用是非const的,因此不會綁定到臨時對象。 因此,傳遞的變量的類型必須完全匹配。 不接受涉及隱式轉換的近似匹配,因為隱式轉換的結果是臨時的。

另一方面,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)

這是非常(!)糟糕的設計。 這段代碼很糟糕,而且非常難以理解和維護。 這個問題是這個問題的延續: C ++類,面向對象編程

現在你在解決症狀,而不是真正的問題。 而真正的問題是用C ++術語來思考而不是用C語言(如果你想成為C ++面向對象的程序員)。

這里有效的C ++代碼(C ++,而不是帶有類的C):

#include <string>

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

就這樣。 所有其他的東西(包括copy contstructor)C ++編譯器為你生成(和你自己的手動實現一樣有效)! 這更簡單,更清晰,更易於維護和理解,首先是:bug free;)。 這是真正的C ++代碼。

其他人已經正確回答了你的問題,但是到目前為止你似乎還沒有理解它,所以我會盡量讓你盡可能清楚。

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

該函數期望第二個參數是對const指針的非const引用(而不是您可能想到的對指針的const引用!)。

當你試圖調用這個函數作為第二個參數傳遞一個指針(不是一個const指針)時,編譯器無法為它創建一個引用,只是因為它希望綁定一個對const指針的引用,並且不允許它隱式地將指針強制轉換為const指針,因為非const引用可能不會綁定到rvalues(臨時值)。

如果希望函數期望對const指針的const引用,則必須更改其簽名,如下所示:

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

這里重要的是要理解編譯器在綁定引用之前隱式地將提供的指針強制轉換為const指針,這在本例中是允許的,因為const引用可以綁定到rvalues和lvalues。

類似地,如果您希望函數期望對指針的const引用(這可能是您的初衷),則簽名應如下所示:

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

這里編譯器不必隱式地轉換任何東西,因為提供的參數已經與引用期望綁定的類型匹配。

我希望我為你清楚明確地說明了這一點,並且你正確地理解了導致問題的原因,這確實很重要,但是,我強烈建議你不要寫這樣的代碼,而是要遵循以下建議。其他。

其他人指出你應該用指針替換引用。

以下是一些其他但相關的評論:

  1. 如果定義了復制構造函數,也要定義賦值運算符。 在大多數情況下,它們應該一起出現。

  2. 將單個參數構造函數聲明為explicit是一種很好的做法。

  3. 將對象命名為對象是一個不好的約定,可能會導致混淆。 很明顯,每個類型的人都是一個對象。 使用更有意義的名稱,如person(const person& other); person(const person& rhs); // after right-hand-side person(const person& rhs); // after right-hand-side

  4. 使用std::string 如果你是用C ++編程,沒有理由不使用std::string和juggle C-strings。

  5. 最后,注意異常安全,遵循最佳實踐,例如復制以非投擲交換操作實現的操作。 請參閱文章異常安全類設計,第1部分: Herb Sutter的復制分配

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM