动态内存和类

时间:2016-11-22 23:27:43

标签: c++

我试图在析构函数中使用delete[]来删除已创建的指针。使用delete[] string;时,我得到了垃圾。如果没有delete,我的代码就会出现:

class test
{
public:
    test operator=(char* tests)
    {
        this->string = tests;
        return *this;
    }

    int starlen()
    {
        i = 0;
        while (this->string[i] != '\0')
            i++;

        return i;
    }

    test operator+(test& tests)
    {
        a = strlen(this->string);
        b = strlen(tests.string);

        ptr = this->string;
        ptr2 = tests.string;

        for (int i = a; i < a + b; i++)
        {
            *(ptr + i) = *(ptr2 + i - a);
        }

        ptr[a + b] = '\0';
        return ptr;
    }

    void printit()
    {
        cout << string;
    }

    test()
    {
        string = 0;
    }

    test(char* ptr)
    {
        string = new char[10];
        strcpy(string, ptr);
    }

    ~test()
    {
        //delete[] string; ????
    }

public:
    char* string;
    int a;
    int b;
    char* ptr;
    char* ptr2;
    int i;
};

int main()
{
    test t1("book");
    test t2("shelf");
    test t3;
    t3 = t1 + t2;
    cout << t3.starlen() << endl;

    t3.printit();

    return 0;
}

2 个答案:

答案 0 :(得分:3)

您的析构函数失败,因为您没有正确关注Rule of Three

  

三规则(也称为三巨头或三巨头的规则)是C ++(在C ++ 11之前)的经验法则,声称如果一个类定义了一个(或更多)以下它应该明确定义所有三个:

     
      
  •   
  • 复制构造函数
  •   
  • 复制分配运算符
  •   
  1. 您的类缺少复制构造函数,它接受test个对象作为输入。您有一个转换构造函数,它接受char*作为输入,但如果该输入超过9个字符,则在复制字符时会丢弃周围的内存。

  2. 您的班级缺少一个接受test对象作为输入的复制赋值运算符。您有转换分配运算符,它接受char*作为输入,但它正在使用char*所有权而不是复制字符数据,它正在泄漏先前分配的string缓冲区。更糟糕的是,它返回正在修改的test对象的副本,而不是将引用返回给对象,并且该副本不起作用正确的,因为你缺少复制构造函数。因此,最终会有多个test对象指向内存中相同的string缓冲区,因此您将在同一内存中多次调用delete[]析构函数。

  3. 此外,您的operator+也未正确实施。它返回一个新的test对象,但它没有分配占两个源字符串总长度的新内存。您只需将右侧test对象中的字符数据直接复制到左侧char*对象的test字符串中,而不先将其展开。所以你在摧毁周围的记忆。这个operator+应该返回一个新的test对象,它是连接在一起的输入字符串的副本,而根本不修改任何一个源字符串。

    尝试更像这样的东西:

    class test
    {
    public:
        // default constructor
        test()
            : string(0)
        {
        }
    
        // copy constructor
        test(const test &src)
            : string(new char[src.starlen()+1])
        {
            strcpy(string, src.string);
        }
    
        // converting constructor
        test(const char* src)
            : string(new char[strlen(src)+1])
        {
            strcpy(string, src);
        }
    
        // destructor
        ~test()
        {
            delete[] string;
        }
    
        // copy assignment operator
        test& operator=(const test &rhs)
        {
            if (&rhs != this)
                std::swap(string, test(rhs).string);
            return *this;
        }
    
        // converting assignment operator
        test& operator=(const char *rhs)
        {
            std::swap(string, test(rhs).string);
            return *this;
        }
    
        int starlen() const
        {
            return strlen(string);
        }
    
        test operator+(const test& rhs) const
        {
            int a = starlen();
            int b = rhs.starlen();
    
            test ret;
            ret.string = new char[a+b+1];
            strcpy(ret.string, string);
            strcpy(ret.string+a, rhs.string);
    
            return ret;
        }
    
        void printit() const
        {
            std::cout << string;
        }
    
    private:
        char* string;
    };
    
    int main()
    {
        test t1("book");
        test t2("shelf");
        test t3;
        t3 = t1 + t2;
        cout << t3.starlen() << endl;
    
        t3.printit();
    
        return 0;
    }
    

    如果您使用的是C ++ 11或更高版本,您也应该关注Rule of Five

      

    随着C ++ 11的出现,C ++ 11实现了移动语义,允许目标对象从临时对象中抓取(或窃取)数据,因此可以将规则扩展为五。以下示例还显示了新的移动成员:移动构造函数和移动赋值运算符。因此,对于五的规则,我们有以下特殊成员:

         
        
    •   
    • 复制构造函数
    •   
    • 移动构造函数
    •   
    • 复制分配操作员
    •   
    • 移动分配运算符
    •   
    class test
    {
    public:
        // default constructor
        test()
            : string(0)
        {
        }
    
        // copy constructor
        test(const test &src)
            : string(new char[src.starlen()+1])
        {
            strcpy(string, src.string);
        }
    
        // converting constructor
        test(const char* src)
            : string(new char[strlen(src)+1])
        {
            strcpy(string, src);
        }
    
        // move constructor
        test(test &&src)
            : string(0)
        {
            std::swap(string, src.string);
        }
    
        // destructor
        ~test()
        {
            delete[] string;
        }
    
        // copy assignment operator
        test& operator=(const test &rhs)
        {
            if (&rhs != this)
                std::swap(string, test(rhs).string);
            return *this;
        }
    
        // converting assignment operator
        test& operator=(const char *rhs)
        {
            std::swap(string, test(rhs).string);
            return *this;
        }
    
        // move assignment operator
        test& operator=(test &&rhs)
        {
            std::swap(string, rhs.string);
            return *this;
        }
    
        int starlen() const
        {
            return strlen(string);
        }
    
        test operator+(const test& rhs) const
        {
            int a = starlen();
            int b = rhs.starlen();
    
            test ret;
            ret.string = new char[a+b+1];
            strcpy(ret.string, string);
            strcpy(ret.string+a, rhs.string);
    
            return ret;
        }
    
        void printit() const
        {
            std::cout << string;
        }
    
    private:
        char* string;
    };
    

    话虽这么说,这可能只是一个学习如何编写自定义字符串类的练习。但是,C ++标准定义了std::string类,您应该使用它。如果你想要自己的类,你可以委托给std::string,让编译器和STL为你做繁重的工作:

    class test
    {
    public:
        // default constructor
        test()
        {
        }
    
        // copy constructor
        test(const test &src)
            : str(src.str)
        {
        }
    
        // converting constructor
        test(const std::string &src)
            : str(src)
        {
        }
    
        // move constructor
        test(test &&src)
            : str(std::move(src.str))
        {
        }
    
        int starlen() const
        {
            return str.length();
        }
    
        test operator+(const test& rhs) const
        {
            return str + rhs.str;
        }
    
        void printit() const
        {
            std::cout << str;
        }
    
    private:
        std::string str;
    };
    

答案 1 :(得分:0)

删除内部字符串的责任是谁?我注意到你将字符串的内存分配给没有构造函数的t3,然后在两次相同的内存地址上调用析构函数。此外,你的算子+,应对很可能会写入传递给它的数组的末尾。你至少应该有一个strlen()结束检查,以防止发生。如果你的缓冲区已经损坏,那么任何对delete []的调用都会因堆上的内存覆盖而抱怨。

请参阅Remy的答案,找到一个更好的解决方案,正确处理字符串操作和内存分配问题(非常复杂)的性质。从我能看到的情况来看,答案甚至是例外。