strlen接收NULL而不是正确的输入

时间:2017-02-26 03:05:48

标签: c++ memory-management valgrind

我正在实现类似模板矢量的类。我希望我的向量在intProduct类上运行,该类在main中定义。它适用于int,但Product类Valgrind报告以下内容:

==22629== Memcheck, a memory error detector
==22629== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==22629== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==22629== Command: ./a.out
==22629== v2 = ==22629== Invalid read of size 1
==22629==    at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==22629==    by 0x4018DF: Product (main.cpp:33)
==22629==    by 0x4018DF: my_vector (my_vector.h:24)
==22629==    by 0x4018DF: void test_my_vector<Product>(Product, Product) (main.cpp:100)
==22629==    by 0x400E67: main (main.cpp:175)
==22629==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==22629== 
==22629== 
==22629== Process terminating with default action of signal 11 (SIGSEGV)
==22629==  Access not within mapped region at address 0x0
==22629==    at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==22629==    by 0x4018DF: Product (main.cpp:33)
==22629==    by 0x4018DF: my_vector (my_vector.h:24)
==22629==    by 0x4018DF: void test_my_vector<Product>(Product, Product) (main.cpp:100)
==22629==    by 0x400E67: main (main.cpp:175)
==22629==  If you believe this happened as a result of a stack
==22629==  overflow in your program's main thread (unlikely but
==22629==  possible), you can try to increase the size of the
==22629==  main thread stack using the --main-stacksize= flag.
==22629==  The main thread stack size used in this run was 8388608.

好吧,似乎我将NULL传递给了strlen,但是由于我传递了NULL并且我没有任何Product构造函数,这些构造函数用name_填充NULL字段,它意味着我已经删除了对象,它正被传递给复制构造函数。至少这些是我的想法。

但我真的不明白我的错误在哪里。也许是因为placement new或其他一些内存管理错误的使用不当。

我还有test_my_vector函数,用于测试我的矢量。

的main.cpp

#include <iostream>
#include <fstream>
#include <cstring>
#include <assert.h>
#include "my_vector.h"
using namespace std;
class Product {
public:
    Product(const char* name, int quantity, double price){
        name_ = new char[strlen(name) + 1];
        strcpy(name_, name);
        quantity_ = quantity;
        price_ = price;
    }


    Product(){
        name_ = new char[strlen("") + 1];
        strcpy(name_, "");
        quantity_ = 0;
        price_ = 0;
    }

    ~Product(){
        delete [] name_;
    }

    Product(const Product& other){
        price_ = other.price_;
        quantity_ = other.quantity_;
        name_ = new char[strlen(other.name_) + 1];
        strcpy(name_, other.name_);
    };

    const Product& operator=(const Product& other){
        delete [] name_; 
        price_ = other.price_;
        quantity_ = other.quantity_;
        name_ = new char[strlen(other.name_) + 1];
        strcpy(name_, other.name_);
        return other;
    };


private:
    char* name_;
    int quantity_;
    double price_; 
};



template <typename T>  
void test_my_vector(T t1, T t2){
   //some asserts
}

int main() {
    test_my_vector<int>(5, 10);
    test_my_vector<Product>(Product("asdf", 4, 12.0), Product("qwe", -1, 7.5));
    return 0;
 }

和my_vector.h

template <class T>
class my_vector{
public:
my_vector(){
    capacity_ = 0;
    size_ = 0;
    array_ = NULL;
};

my_vector(const size_t n){
    size_ = n;
    capacity_ = (size_t) pow(2, ceil(log(n)/log(2)));
    array_ = (T*)(new char[sizeof(T) * capacity_]());
};

my_vector(const my_vector<T>& other){
    size_ = other.size_;
    capacity_ = other.capacity_;
    array_ = (T*)(new char[sizeof(T) * capacity_]);
    for(size_t i = 0; i < size_; i++)
        new (&array_[i]) T(other.array_[i]), std::cout << i;
};

my_vector<T>& operator=(const my_vector<T>& other){
    this -> ~my_vector();
    size_ = other.size_;
    capacity_ = other.capacity_;
    array_ = (T*)(new char[sizeof(T) * capacity_]);
    for(size_t i = 0; i < size_; i++)
        new (&array_[i]) T(other.array_[i]);;
    return *this;
};

~my_vector(){
    for(size_t i = 0; i < size_; i++)
        array_[i].~T();
    delete [] (char*) array_;
};



void resize(const size_t n){
    reserve(n);
    size_ = n;
};

void reserve(const size_t n){
    if (n > capacity_){
        if(!capacity_) capacity_ = 1;
        while(capacity_ < n)
            capacity_ *= 2;
        T* new_array = (T*)(new char[sizeof(T) * capacity_]);
        for (size_t i = 0; i < size_; i++)
            new (&new_array[i]) T(array_[i]);;
        this -> ~my_vector();
        array_ = new_array;
    }
};


void push_back(const T& t){
    if (!capacity_)
        reserve(2);
    else if(size_ == capacity_)
        reserve(2 * capacity_);
    new (&array_[size_]) T(t);
    size_++;
};

private:
   size_t capacity_;
   size_t size_;
   T* array_;
};

1 个答案:

答案 0 :(得分:0)

第一个错误:您的my_vector构造函数不会创建对象。它所做的就是初始化对象的内存,但不构造它们。最后发生的事情是随后的复制操作正在使用无效对象。

更正如下:

my_vector(const size_t n)
{
    size_ = n;
    capacity_ = (size_t)pow(2, ceil(log(n) / log(2)));
    array_ = (T*)(new char[sizeof(T) * capacity_]());
    for (size_t i = 0; i < size_; i++)
        new (&array_[i]) T();  // default construct objects
};

此外,如果您要使用整数指数,我建议不要使用pow。评论中的链接plus this one显示了使用pow进行整数计算时会发生什么。

第二个错误:您的赋值运算符不正确,因为它们不检查自我赋值。保存所有这些麻烦并使用copy / swap idiom作为您的赋值运算符:

#include <algorithm>
//...
Product& operator=(const Product& other)
{
    Product temp(other);
    std::swap(temp.name_, name_);
    std::swap(temp.price, price);
    std::swap(temp.quantity_, quantity_);
    return *this;
}

使用my_vector可以完成同样的事情:

my_vector<T>& operator=(const my_vector<T>& other)
{
    my_vector<T> temp(other);
    std::swap(temp.size_, size_);
    std::swap(temp.capacity_, capacity_);
    std::swap(temp.array_, array_);
    return *this;
}