C ++ Memory Leak char *

时间:2013-12-08 01:54:27

标签: c++

所以我有课:

class Worker{
private:
   char *workerName;
   string SSN;
public:
   Worker();
   Worker(char *, string);
   ~Worker();
   void setWorkerName(char *);
   void setSSN();
   char *getWorkerName();
   string getSSN();
   virtual void printValues();
}

所以我在测试函数中创建了一个类:

int main(void){
   Worker Person("Person", "555-55-5555");
   //call print member function
   return 0;
}

(我也有一个不推荐的从字符串常量到char *的转换,不知道为什么)

我的工作者构造函数是:

Worker::Worker(){
   workerName = new char [40];
   SSN = " ";
}

Worker::Worker(char * name, string SSN){
   workerName = new char [40];
   strcpy(workerName, name);
   this->SSN = SSN;
}

Worker::~Worker(){
   delete[] workerName;
}

当我输入这个时,我现在意识到我可能没有第二个构造函数为char *分配内存。如果这是问题,我该怎么做呢?

3 个答案:

答案 0 :(得分:0)

现在,由于您总是在析构函数中执行delete[] workerName,因此当您调用Person("Person", "555-55-5555");时,您将尝试删除静态分配的字符串。

你应该保持一致。 我建议你分配内存并将输入字符串复制到第二个构造函数中的workerName。

答案 1 :(得分:0)

3的规则规定如果你需要一个析构函数或一个复制构造函数或一个复制赋值运算符,那么很可能你需要这三个。

例如,为了正确处理复制

由于你被限制使用char指针(希望允许char const*?),你可以做的最好的事情是为这样一个指针所代表的字符串值创建一个包装器对象,其中包装器对象负责正确处理逻辑复制。然后使用该包装器对象而不是原始指针。这通常是很好的C ++实践,除了对于字符串,人们会改为使用字符串。 : - )


修正案:考虑到你的(显然不太好)教授允许的评论,请问他是否可以接受以下内容:

#include <algorithm>    // std::swap
#include <string.h>     // ::strcpy, ::strlen

namespace my {
    auto duplicate( char const* const s )
        -> char const*
    { return ::strcpy( new char[strlen( s ) + 1], s ); }

    class String_value
    {
    private:
        char const*     chars_;

    public:
        auto pointer() const
            -> char const*
        { return chars_; }

        void swap_with( String_value& other ) throw()
        { std::swap( chars_, other.chars_ ); }

        void operator=( String_value other )
        { swap_with( other ); }

        ~String_value()
        { delete[] chars_; }

        String_value( char const* const s )
            : chars_( duplicate( s ) )
        {}

        String_value( String_value const& other )
            : chars_( duplicate( other.chars_ ) )
        {}
    };
}  // namespace my

class Worker
{
private:
    my::String_value    name_;
    my::String_value    ssn_;

public:
    auto name() const
        -> char const*
    { return name_.pointer(); }

    auto ssn() const
        -> char const*
    { return ssn_.pointer(); }

    Worker( char const name[], char const ssn[] )
        : name_( name )
        , ssn_( ssn )
    {}
};

#include <ostream>

std::ostream& operator<<( std::ostream& stream, Worker const& w )
{
    return stream << "Worker(" << w.name() << ", " << w.ssn() << ")";
}

#include <iostream>
auto main()
    -> int
{
    using namespace std;
    Worker const person( "Person", "555-55-5555" );
    cout << person << endl;
}

答案 2 :(得分:0)

  1. 您应该只使用std::string表示所有字符串。这会照顾你的记忆问题(至少现在)。
  2. 您的双参数构造函数已损坏。如果要将字符串参数传递给函数,则应使用const string&而不是string。后者制作了一个你不需要的字符串的临时副本。由于string有一个带const char*的构造函数,因此您也可以传入C字符串。 (可能使用const string&&进行C ++ 11优化,但我忘了。)
  3. 当逐字返回字符串时,您也应该返回const string&,以避免制作不必要的副本。返回string的唯一时间是返回临时(例如,string类的二进制+操作被声明为string operator +(const string&),因为返回的字符串既不是左边的 - 手或右手串,但是新的字符串)。
  4. 通过使用string进行存储,您不需要析构函数,因为string在销毁时会释放存储空间。
  5. 您可以在参数列表后标记不会将对象更改为const的方法。这使this成为const Worker*。未以const这种方式标记的方法不能用于const Worker个对象。
  6. 在课堂上定义简单的方法。这使编译器可以轻松地内联它们。
  7. 然后您的代码如下:

    class Worker {
    private:
       string workerName;
       string SSN;
    public:
       Worker() {}
       Worker(const string& workerName_, const string& SSN_)
       : workerName(workerName_), SSN(SSN_) {}
       void setWorkerName(const string& newName) { workerName = newName; }
       void setSSN(const string& newSSN) { SSN = newSSN; }
       const string& getWorkerName() const { return workerName; }
       const string& getSSN() const { return SSN; }
       virtual void printValues() const;
    };
    
    int main(void) {
       Worker Person("Person", "555-55-5555");
       Person.printValues();
       return 0;
    }
    
    void Worker::printValues() const {
        cout << "Name: " << workerName << ", SSN: " << SSN << endl;
    }
    

    好的,所以你的教授希望你使用char*并手工管理。随你。现在你需要析构函数,复制构造函数和赋值。知道什么?让我们放心吧,阻止副本只是因为。

    在C ++中,字符串文字是const char*不是 char*,所以如果你的教授期待char*获得编译器警告。如果他因为const correct而认为你错了,那就把它与老板一起讨论,因为教授是无能的,并且教导的做法会给现实世界带来问题。

    让我们再次浏览代码,将string版本转换为char*版本。第一堂课。因为设置字符串的任何内容都是非常重要的,所以我们的大多数内联方法都不再内联:

    class Worker {
    private:
       char *workerName;
       char *SSN;
       Worker(const Worker&);
       Worker& operator=(const Worker&);
    public:
       Worker();
       Worker(const char *workerName_, const char *SSN_);
       ~Worker();
       void setWorkerName(const char *newName);
       void setSSN(const char *newSSN);
       const char *getWorkerName() const { return workerName; }
       const char *getSSN() const { return SSN; }
       virtual void printValues() const;
    };
    

    除了公共析构函数之外,请注意添加的复制构造函数和equals运算符标记为private。这可以防止Worker被复制,因为没有人可以调用它们。 (这是干杯正在谈论的“三个规则”。)

    现在是你的默认构造函数。你有:

    Worker::Worker(){
       workerName = new char [40];
       SSN = " ";
    }
    

    这里的问题是你将workerName设置为指向新分配的(IIRC)归零内存,并且你将SSN指向一个字符串常量。你应该为两者分配内存,并且内存应该足够大以保存值。这意味着分配一个char的两个数组,其值为'\0'(即一个只是空终止符的字符串):

    Worker::Worker(){
       workerName = new char [1];
       workerName[0] = '\0';
       SSN = new char [1];
       SSN[0] = '\0';
    }
    

    您也可以使用C ++初始化程序语法:

    Worker::Worker(): workerName(new char [1]), SSN(new char [1]) {
       workerName[0] = '\0';
       SSN[0] = '\0';
    }
    

    接下来,你的双参数构造函数。同样的事情,除了你使用strlen来查找字符串的长度和strcpy来复制它们:

    Worker::Worker(const char *workerName_, const char *SSN_) {
       workerName = new char [strlen(workerName_) + 1];
       strcpy(workerName, workerName_);
       SSN = new char [strlen(SSN_) + 1];
       strcpy(SSN, SSN_));
    }
    

    请注意,数组大小为“长度加1”,因为strlen最后不计算'\0'

    另请注意,如果您使用mallocfree就像使用C字符串一样,那么您可以使用C的strdup函数:

    Worker::Worker(const char *workerName_, const char *SSN_)
    : workerName(strdup(workerName_)), SSN(strdup(SSN_)) {
    }
    

    但你不应该使用malloc释放delete[]'内存,而不应该使用new[]释放free内存。

    析构函数实际上很好,只需为SSN添加类似内容:

    Worker::~Worker(){
       delete[] workerName;
       delete[] SSN;
    }
    

    我会将“设置”功能留给读者练习。请记住,他们需要1)删除现有值,2)分配新值。与上述程序相同。

    现在,如果您的教授指出workerNamechar*SSNstring ......那么,混合上述代码来做到这一点是读者的另一个练习