【问题标题】:Constructors taking references in C++在 C++ 中获取引用的构造函数
【发布时间】:2010-05-18 10:41:22
【问题描述】:

我正在尝试创建引用对象的构造函数。使用引用创建对象后,我需要打印两个对象的字段值。然后我必须删除第一个对象,并再次显示两个对象的字段值。我的班级 Person 看起来像这样:

class Person {   
    char* name;
    int age;
public:

    Person(){
        int size=0;
        cout << "Give length of char*" << endl;
        cin >> size;
        name = new char[size];      
        age = 0;
    }

    ~Person(){
        cout << "Destroying resources" << endl;
        delete[] name;
        delete age;
    }  

void init(char* n, int a) {
    name = n;
    age = a;
} 
}; 

这是我的实现(使用函数 show() )。我的教授说,如果这个任务写得正确,它会返回一个错误。

#include <iostream>
using namespace std;

class Person {   
    char* name;
    int age;
public:

    Person(){
        int size=0;
        cout << "Give length of char*" << endl;
        cin >> size;
        name = new char[size];      
        age = 0;
    }

    Person(const Person& p){
        name = p.name;
        age = p.age;
    }

    ~Person(){
        cout << "Destroying resources" << endl;
        delete[] name;
        delete age;
    }  

void init(char* n, int a) {
    name = n;
    age = a;
}


void show(char* n, int a){
    cout << "Name: " << name << "," << "age: " << age << "," << endl; 
}

}; 


int main(void) {
    Person *p = new Person;  
    p->init("Mary", 25);

    p->show();

    Person &p = pRef;
    pRef->name = "Tom";
    pRef->age = 18;

    Person *p2 = new Person(pRef);

    p->show();
    p2->show();

    system("PAUSE");
    return 0;
}

【问题讨论】:

  • [C++ 对象的构造函数][1] [1] 的可能重复:stackoverflow.com/questions/2856156/constructors-for-c-objects
  • 第一优先:删除init 函数并编写适当的构造函数。这是糟糕的 C++,不要走那条路。
  • @Konrad:我不完全确定我是否同意你的看法。有一个很好的论点是在构造函数中做的尽可能少,而在 Init 函数中做更多的工作。除其他外,它允许您返回错误而不诉诸异常处理。那就是说他在构造函数中进行分配,这样就会把我的观点从水中吹出来,因此说我不完全同意;)
  • @Goz:我认为这种模式是错误的。它看起来像C,它可以(应该)是C++。这样做可能会导致未初始化的实例(又名“无效对象”)并且是违反直觉的。使用异常中止构造函数是干净的:如果初始化出错,则对象被正确销毁并且您在捕获的异常中拥有所需的所有信息。另一方面,您可以保证Person 的任何实例都正确构建。
  • 从析构函数中移除delete age;age没有被动态分配,所以你不能删除它。

标签: c++ reference constructor


【解决方案1】:

您的复制构造函数的问题在于它只分配了 p.name:

name = p.name // Now this and p hold a pointer to the same memory

由于thisp 现在都持有指向同一个内存位置的指针,因此第一个销毁的将释放内存,而第二个将持有指向不存在对象的指针。随后使用该指针或删除它将导致未定义的行为。解决方案是为 name 分配一个新数组,并将 p.name 的内容复制到该数组中,这样内存就不会共享了。

同样,您的 init 函数会覆盖 name,忽略内存已分配的事实(这是内存泄漏),也忽略字符串稍后将被破坏的事实(即使调用者可能希望拥有并释放它)字符串本身)。另外,我应该指出,您的 show 函数采用参数“n”,但使用“name”。你的 show 函数可能不应该带任何参数(实际上,你调用它的方式暗示它没有),因为你的类中已经存在所有需要的字段(或者你可能打算让它成为一个独立的函数这需要类的字段?)。您应该再次查看您的代码是否有其他错误。

【讨论】:

  • 比这更糟糕。当第二个对象销毁时,它将删除一个已经被删除的指针。如果幸运的话,操作系统会发现并终止释放它不拥有的地址的进程;如果你不那么幸运,所有的地狱都会悄悄地松动。此外,没有赋值运算符(或者,更确切地说,存在不合适的默认赋值运算符)。
  • @Wilhelm,它不会比未定义的行为更糟​​糕。未定义的行为是未定义的行为,无论哪种方式,它都可能爆炸。也就是说,你是对的,还有其他错误。我只是查看了复制构造函数,因为这是问题中唯一提到的内容。
【解决方案2】:

首先,尝试编译你的代码(在发布到 SO 之前编译代码通常是个好主意。)它包含几个错误,编译器会显示它们。第二部分将更改以下内容:

p->init("Mary", 25);

{
  std::string mary("Mary");
  p->init(mary.c_str(), 25);
}

它应该在运行时给你一个错误,它会让你有机会在你的实现中发现一个问题。

【讨论】:

  • 如果我是教授并且我正试图教他们一些关于 char* 字符串、指针和内存泄漏的知识,并且有人在我没有教的时候转而使用 std::string然而,我不会高兴。虽然 STL 解决了现实生活中的许多问题,但在有关指针的作业中随意使用它,因为指针很难,在课程中并不是一个好的策略。
  • std::string 此处仅表明指向char* 的指针可能会失效。请注意,我使用大括号来限制变量mary 的范围。在此之前,OP 使用指向字符串文字的指针,该字符串在程序运行时始终有效。
  • 我尝试在我的答案中添加提示,以便 OP 可以自行找到所有错误。
猜你喜欢
  • 2010-11-27
  • 2017-05-28
  • 1970-01-01
  • 2019-04-16
  • 2015-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多