查看以下代码。这有什么问题?编译器给出了这个错误:
在复制构造函数
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);
}
从这个问题的答案来看,到目前为止我所理解的是: 编译器不允许将引用转换为常量引用,因为引用已经是常量。它们不能指向像指针一样的不同内存位置。我是对的吗?
答案 0 :(得分:10)
这不会更好吗?
class person
{
private:
std::string name;
std::string fathername
std::string address;
};
// constructor and copy constructor autogenerated!
这种方式更像是“C ++”;)。
答案 1 :(得分:7)
除非您计划更改指针,否则不应将引用传递给指针:
变化:
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包装在另一个类中,以保护它免受异常的问题。
答案 2 :(得分:4)
更改
person :: person(person& object)
到
person :: person(const person& object)
首发......
答案 3 :(得分:3)
编译器告诉你问题 - 更改签名以接受2个char *指针(而不是1个const char *),它应该编译。
问题实际上是由于使用了引用 - 如果你创建了一个简单地使用2个char *指针(而不是引用)的复制方法,那么编译器将自动识别从char *到const char *的转换并使用方法。由于您只有一个接受对不同类型的引用的方法,因此无法自动执行此操作。
答案 4 :(得分:1)
我感觉很慷慨,所以这里是你的代码的更正的版本:
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);
}
你能否识别潜伏的瑕疵或安全物品?
答案 5 :(得分:1)
正如其他人所说,如果你不打算修改它,你不应该通过引用传递char指针。
问题是引用是非const的,因此不会绑定到temporaries。因此,传递的变量的类型必须完全匹配。不接受涉及隐式转换的近似匹配,因为隐式转换的结果是临时的。
另一方面,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)
答案 6 :(得分:1)
这是非常(!)糟糕的设计。这段代码很糟糕,而且非常难以理解和维护。 这个问题是这个问题的延续:C++ classes , Object oriented programming。
现在你在症状上挣扎,而不是真正的问题。真正的问题是用C ++术语来思考而不是用C语言(如果你想成为C ++面向对象的程序员)。
有效的C ++代码(C ++,而不是带有类的C):
#include <string>
class person
{
public:
person();
private:
std::string name, fathername, address;
};
多数民众赞成。所有其他的东西(包括copy contstructor)C ++编译器为你生成(和你自己的手动实现一样有效)!这更简单,更清晰,更易于维护和理解,首先是:bug free;)。这是真正的C ++代码。
答案 7 :(得分:1)
其他人已经正确回答了你的问题,但到目前为止你似乎并不理解,所以我会尽量让你明白。
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)
这里编译器不必隐式地转换任何东西,因为提供的参数已经与引用期望绑定的类型匹配。
我希望我为你清楚明确地说明了这一点,并且你正确地理解了导致问题的原因,这确实很重要,但是,我强烈建议你不要写这样的代码,而是要遵循建议由其他人给出。
答案 8 :(得分:0)
其他人指出你应该用指针替换引用。
以下是其他一些但相关的评论:
如果定义了复制构造函数,也可以定义赋值运算符。在大多数情况下,它们应该一起出现。
将单个参数构造函数声明为explicit
是一个好习惯。
将对象命名为对象是一个不好的约定,可能会导致混淆。很明显,每个类型的人都是一个对象。使用更有意义的名称,例如person(const person& other);
或person(const person& rhs); // after right-hand-side
使用std::string
。如果您使用C ++编程,则没有
理由不使用std::string
并改为使用C字符串。
最后,请注意异常安全,遵循最佳实践,例如复制根据非投掷交换操作实施的操作。参见Herb Sutter的文章Exception-Safe Class Design, Part 1: Copy Assignment