3

在我的大学里,有一个用 C++ 进行的实用编程测试——我遇到了一个例子,我不确定所讨论的任务是否有效并且是否可以正确完成。

(简单)任务:

  • 完成 的析构函数Person,以便name再次释放分配的

  • 在 main 函数中,替换//???为释放先前分配的内存所需的语句

起初,这些任务对我来说似乎微不足道:对于析构函数,只需编写delete[] name并在 main 函数中使用delete[] friends. 大概这也是这个例子的作者想要我们做的。

然而:

此代码示例中似乎存在缺陷,导致内存泄漏以及多次调用析构函数。

person 类没有赋值operator =,这意味着由于现有的 Person 对象如maria被分配到 main 函数中的数组中的槽friends,内部分配name的 s 不会被复制。所以现在两个对象共享同一个内部char*指针!此外,指向Person先前驻留在所述数组槽中的名称的指针永久丢失,导致不可避免的内存泄漏。

正如delete[] friends;所谓的 - 数组中的对象被销毁 - 导致它们的析构函数被调用并释放它们的name成员。但是,当程序结束时,范围内的本地 Person 对象main被破坏 - 当然它们的name成员仍然指向之前已经释放的内存。

实际问题:

  • 这个测试示例有缺陷,还是我遗漏了什么?
  • 如果完全坚持执行给定的任务(仅更改析构函数的实现,并在主函数的注释部分插入新代码),是否可以解决上面列出的问题?

..

#include <iostream>
using namespace std;

int strlen(const char *str) {
    if (str==0) return 0;
    int i=0;
    for (; str[i]; ++i);
    return i;
}

void strcpy(const char *src, char *dest) {
    if (src==0 || dest==0) return;
    int i=0;
    for (; src[i]; ++i) dest[i]=src[i];
    dest[i]=’\0’;
}

class Person {
    char *name;
public:
    Person(const char *str = "Susi") {
        name = new char[strlen(str)+1];
        strcpy(str,name);
    }

    Person(const Person &p) {
        name = new char[strlen(p.name)+1];
        strcpy(p.name,name);
    }

    ~Person() {
        //...
    }

    void change() {
        name[4]='e';
    }

    ostream &print(ostream &o) const {
        o<<name;
        return o;
    }
};

int main() {
    Person maria("Maria"), peter("Peter"), franz("Franz"), luisa("Luisa");
    Person mary(maria);
    Person luise;
    Person p(luise);

    Person *friends= new Person[7];
    friends[0]=maria;
    friends[1]=peter;
    friends[2]=franz;
    friends[3]=luisa;
    friends[4]=mary;
    friends[5]=luise;
    friends[6]=p;
    friends[5]=luisa;
    friends[3].change();
    friends[4].change();

    for (int i=0; i<7; ++i) {
    friends[i].print(cout);
    cout<<endl;
    }

    //???
    return 0;
}
4

3 回答 3

5

你是绝对正确的。您可以通过仅在指定位置进行更改来修复它,但是它们将相当极端:

//...将析构函数内部替换为:

    delete[] name;
}

Person& operator=(const Person& other)
{
    if (this != &other) {
        delete[] name;  // not completely exception-safe!
        name = new char[strlen(other.name)+1];
        strcpy(other.name,name);
    }
    return *this;

另一个严重的问题是使用重新strcpy排序参数的新定义重新定义标准函数 ()。

(另请参阅:SQL 注入攻击,这也会导致现有的语法元素对、频繁的引号和括号与插入的语法元素重新配对)

于 2012-10-08T16:51:52.060 回答
2
  1. 是的,测试示例有缺陷,可能是有意识的。类Person肯定需要赋值运算符,记住三法则
  2. 不,这是不可能的。默认编译器生成的赋值运算符将泄漏friends数组中的对象分配的内存,并双重删除自动Person对象分配的内存。
于 2012-10-08T16:52:18.627 回答
-1

对于每一个新的,都应该有一个 delete[]。

于 2012-10-08T16:58:04.563 回答