释放内存的问题(对于包含自定义向量的类)

问题描述 投票:0回答:1
#include "../include/Hex.hpp"

int main()
{
    Vector<unsigned char> a{'1', '2', '3', '4'};
    Vector<unsigned char> b = a;
    b.resize(2);
    a.resize(6);
    a = b;
}

如果我只是使用向量,那么 valgrind 会写入内存正在被正确释放,无论我使用什么自定义向量方法 但是:

#include "../include/Hex.hpp"

int main()
{
    Hex a("A1");
    Hex b(a);
    Hex c = a;
    std::cout << c;
}
valgrind --tool=memcheck ./main
==9066== Memcheck, a memory error detector
==9066== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9066== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==9066== Command: ./main
==9066== 
==9066== Invalid write of size 2
==9066==    at 0x48529E3: memmove (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==9066==    by 0x10C5CE: unsigned char* std::__copy_move<false, true, std::random_access_iterator_tag>::__copy_m<unsigned char>(unsigned char const*, unsigned char const*, unsigned char*) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10C58A: unsigned char* std::__copy_move_a2<false, unsigned char const*, unsigned char*>(unsigned char const*, unsigned char const*, unsigned char*) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10C4AD: unsigned char* std::__copy_move_a1<false, unsigned char const*, unsigned char*>(unsigned char const*, unsigned char const*, unsigned char*) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10C271: unsigned char* std::__copy_move_a<false, unsigned char const*, unsigned char*>(unsigned char const*, unsigned char const*, unsigned char*) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10BF58: unsigned char* std::copy<unsigned char const*, unsigned char*>(unsigned char const*, unsigned char const*, unsigned char*) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10B968: Vector<unsigned char, Allocator<unsigned char> >::operator=(Vector<unsigned char, Allocator<unsigned char> > const&) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10A8F9: Hex::Hex(Hex const&) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10B3B4: main (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==  Address 0x4dddd10 is 0 bytes after a block of size 0 alloc'd
==9066==    at 0x4849013: operator new(unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==9066==    by 0x10BD69: Allocator<unsigned char>::Allocate(unsigned long) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10B6BB: Vector<unsigned char, Allocator<unsigned char> >::Vector() (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10A8DE: Hex::Hex(Hex const&) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10B3B4: main (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066== 
==9066== Invalid read of size 1
==9066==    at 0x10AFCF: operator<<(std::ostream&, Hex&) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10B3E0: main (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==  Address 0x4dddd51 is 1 bytes after a block of size 0 alloc'd
==9066==    at 0x4849013: operator new(unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==9066==    by 0x10BD69: Allocator<unsigned char>::Allocate(unsigned long) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10B6BB: Vector<unsigned char, Allocator<unsigned char> >::Vector() (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10A8DE: Hex::Hex(Hex const&) (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066==    by 0x10B3CA: main (in /home/aleksandr/LABS-OOP/OOPLab2/tests/main)
==9066== 
A1==9066== 
==9066== HEAP SUMMARY:
==9066==     in use at exit: 0 bytes in 1 blocks
==9066==   total heap usage: 6 allocs, 5 frees, 73,732 bytes allocated
==9066== 
==9066== LEAK SUMMARY:
==9066==    definitely lost: 0 bytes in 1 blocks
==9066==    indirectly lost: 0 bytes in 0 blocks
==9066==      possibly lost: 0 bytes in 0 blocks
==9066==    still reachable: 0 bytes in 0 blocks
==9066==         suppressed: 0 bytes in 0 blocks
==9066== Rerun with --leak-check=full to see details of leaked memory
==9066== 
==9066== For lists of detected and suppressed errors, rerun with: -s
==9066== ERROR SUMMARY: 6 errors from 2 contexts (suppressed: 0 from 0)

注意,如果Hex对象是通过复制构造函数创建的,那么内存会正常释放,所以6次分配,5次释放

十六进制.hpp

#ifndef HEX_HPP
#define HEX_HPP

#include <iostream>
#include "Vector.hpp"

class Hex
{
public:
    Vector<unsigned char> hex;
public:
    Hex();
    Hex(const std::string& num);
    Hex(const size_t & n, const unsigned char &t = 0);
    Hex(const Hex& other);
    Hex(Hex&& other) noexcept;
    Hex(const std::initializer_list<unsigned char> &t);

    virtual ~Hex() noexcept;

    int hexToDecimal(char symb);
    char decimalToHex(int dec);

    bool operator>=(const Hex &r) const;
    bool operator<=(const Hex &r) const;
    bool operator<(const Hex &r) const;
    bool operator>(const Hex &r) const;
    bool operator==(const Hex &r) const;
    bool operator!=(const Hex &r) const;
    void operator=(const Hex& other);
    Hex operator+(const Hex& other);
    Hex operator-(const Hex& other);

    friend std::ostream &operator<<(std::ostream &out,  Hex &x);
    friend std::istream &operator>>(std::istream &in,  Hex &x);
};

#include "../src/Hex.cpp"

#endif //HEX_HPP

十六进制.cpp

#include "../include/Hex.hpp"

Hex::Hex() {}

Hex::Hex(const std::string& num)
{
    size_t n = num.size();
    hex.resize(n);
    size_t j = n - 1;
    for(size_t i = 0 ; i < n  ; ++i) {
        if(num[j] >= '0' && num[j] <= '9') {
            hex[i] = num[j];
        }
        else if(num[j] >= 'A' && num[j] <= 'F') {
            hex[i] = num[j];
        }
        else {
            throw "Invalid number";
        }
        --j;
    }
}

Hex::Hex(const size_t & n, const unsigned char &t) 
{
    if(t >= '0' && t <= '9') {
    }
    else if(t >= 'A' && t <= 'F') {
    }
    else {
        throw "Invalid number";
    }
    this->hex.resize(n);
    for(size_t i = 0 ; i < n ; ++i) {
        hex[i] = t;
    }
}

Hex::Hex(Hex&& other) noexcept
{
    this->hex = other.hex;
}

Hex::Hex(const Hex& other)
{
    this->hex = other.hex;
}

Hex::Hex(const std::initializer_list<unsigned char> &t)
{
    hex.resize(t.size());
    int i = t.size();
    for (auto it = t.begin(); it != t.end() ; ++it) {
        unsigned char p = *it;
        --i;
        if(p >= '0' && p <= '9') {
            hex[i] = p;
        }
        else if(p >= 'A' && p <= 'F') {
            hex[i] = p;
        }
        else {
            throw "Invalid number";
        }
    }
}

int Hex::hexToDecimal(char symb)
{
    if(symb >= '0' && symb <= '9') {
        return symb - '0';
    }
    else if(symb >= 'A' && symb <= 'F') {
        return symb - 'A' + 10;
    } else {
        throw "Invalid number";
    }
}

char Hex::decimalToHex(int dec)
{
    if(dec >= 0 && dec <= 9) {
        return dec + '0';
    }
    else {
        return dec - 10 + 'A';
    }
}

Hex Hex::operator+(const Hex& other)
{
    int carry = 0;
    int i = 0;
    int sum = 0;
    Hex temp;
    int j = std::max(other.hex.size(), this->hex.size());
    temp.hex.resize(j);
    while(i < j) {
        if(i < other.hex.size()) {
            sum += hexToDecimal(other.hex[i]);
        }
        if(i < this->hex.size()) {
            sum += hexToDecimal(this->hex[i]);
        }
        sum += carry;
        carry = 0;
        if(sum >= 16) {
            carry = 1;
            sum %= 16;
        }
        temp.hex[i] = decimalToHex(sum);
        sum = 0;
        ++i;
    }
    if(carry) {
        temp.hex.push_back(carry + '0');
    }
    return temp;
}

Hex Hex::operator-(const Hex& other)
{
    int takeUnit = 0;
    int i = 0;
    int sum = 0;
    int del2;
    int del1;
    Hex temp;

    int j = std::max(other.hex.size(), this->hex.size());
    while(i < j) {
        if(i < other.hex.size()) {
            del1 = hexToDecimal(other.hex[i]);
        }
        if(i < this->hex.size()) {
            del2 = hexToDecimal(this->hex[i]);
        }
        sum = del2 - del1 - takeUnit;
        takeUnit = 0;
        if(sum < 0 && i+1 != this->hex.size()) {
            takeUnit += 1;
            sum += 16;
        }
        temp.hex.push_back(decimalToHex(sum));
        ++i;
        sum = 0;
    }
    while(temp.hex.back() == '0' && temp.hex.size() > 1) {
        temp.hex.resize(temp.hex.size()-1);
    }
    return temp;
}

std::ostream &operator<<(std::ostream &out,  Hex &x) 
{
    for(long long int i = x.hex.size() - 1 ; i >= 0 ; --i) {
        out << x.hex[i];
    }
    return out;
}

void Hex::operator=(const Hex &other) 
{
    hex = other.hex;
}

bool Hex::operator>=(const Hex &r) const
{
    if(this->hex.size() < r.hex.size()) {
        return false;
    }
    else if(this->hex.size() > r.hex.size()) {
        return true;
    }
    else {
        for(long long int i = this->hex.size() ; i >= 0 ; ++i) {
            if(this->hex.at(i) < r.hex.at(i)) {
                return false;
            }
        }
        return true;
    }
}

bool Hex::operator<=(const Hex &r) const
{
    if(this->hex.size() < r.hex.size()) {
        return true;
    }
    else if(this->hex.size() > r.hex.size()) {
        return false;
    }
    else {
        for(long long int i = this->hex.size() ; i >= 0 ; ++i) {
            if(this->hex.at(i) > r.hex.at(i)) {
                return false;
            }
        }
        return true;
    }
}

bool Hex::operator<(const Hex &r) const 
{
    return !(*this >= r);
}

bool Hex::operator>(const Hex &r) const 
{
    return !(*this <= r);
}

bool Hex::operator==(const Hex &r) const 
{
    return this->hex == r.hex;
}

bool Hex::operator!=(const Hex &r) const 
{
    return this->hex != r.hex;
}

Hex::~Hex() noexcept 
{
    this->hex.~Vector();
}

Vector 的析构函数

template<typename T, typename A>
Vector<T, A>::~Vector() noexcept
{
    if (data_ != nullptr) {
        for (size_t i = 0; i < size(); ++i) {
            allocator_.Destroy(&data_[i]);
        }
        if (capacity() > 0) {
            allocator_.Deallocate(data(), capacity());
        }
        data_ = nullptr;
    }
    capacity_ = 0;
}

向量析构函数在处理向量本身时可以正常工作,只有十六进制类会破坏所有内容

矢量.cpp

#include "../include/Vector.hpp"
#include <iostream>
#include <limits>
#include <memory>

template<class T, class A>
Vector<T, A>::Vector() : size_(0), capacity_(0)
{
    data_ = allocator_.Allocate(capacity());
}

template<class T, class A>
Vector<T, A>::Vector(size_t size, const T &value) : size_(size), capacity_(0)
{
    capacity_ = CalculateCapacity(size);
    data_ = allocator_.Allocate(capacity());
    std::fill_n(data(), size, value);
}

template<class T, class A>
Vector<T, A>::Vector(const Vector &other)
{
    this->resize(other.size());
    capacity_ = other.capacity();
    std::copy(other.begin(), other.end(), data());
}

template<class T, class A>
Vector<T, A>::Vector(Vector &&other) noexcept
{
    std::swap(size_, other.size_);
    std::swap(capacity_, other.capacity_);
    std::swap(data_, other.data_);
    other.clear();
}

template<class T, class A>
Vector<T, A>::Vector(const std::initializer_list<T> &init)
{
    size_ = init.size();
    capacity_ = CalculateCapacity(size());
    data_ = allocator_.Allocate(capacity());
    std::copy(init.begin(), init.end(), data());
}

template<typename T, typename A>
Vector<T, A>::~Vector() noexcept
{
    if (data_ != nullptr) {
        for (size_t i = 0; i < size(); ++i) {
            allocator_.Destroy(&data_[i]);
        }
        if (capacity() > 0) {
            allocator_.Deallocate(data(), capacity());
        }
        data_ = nullptr;
    }
    capacity_ = 0;
}

template<typename T, typename A>
void Vector<T, A>::push_back(T &value)
{
    emplace_back(std::move(value));
}

template<class T, class A>
void Vector<T, A>::push_back(T &&value)
{
    emplace_back(std::move(value));
}

template<typename T, typename A>
void Vector<T, A>::reallocate(size_t minSize)
{
    const size_t newCapacity = CalculateCapacity(minSize + 1);
    T *newData = allocator_.Allocate(newCapacity);
    for (size_t i = 0; i < size(); ++i) {
        allocator_.Construct(&newData[i], data_[i]);
        allocator_.Destroy(&data_[i]);
    }
    if (capacity() > 0) {
        allocator_.Deallocate(data(), capacity());
    }
    capacity_ = newCapacity;
    data_ = newData;
}

template<typename T, typename A>
[[nodiscard]] size_t Vector<T, A>::CalculateCapacity(size_t minSize) const
{
    std::size_t newCapacity;
    if (minSize < capacity()) {
        newCapacity = 1;
    }
    else if (capacity()) {
        newCapacity = capacity();
    }
    else {
        newCapacity = 1;
    }
    while (newCapacity < minSize) {
        if (newCapacity >= std::numeric_limits<size_t>::max() - newCapacity) {
            return std::numeric_limits<size_t>::max();
        }
        newCapacity *= 2;
    }
    return newCapacity;
}

template<typename T, typename A>
size_t Vector<T, A>::size() const
{
    return size_;
}

template<typename T, typename A>
size_t Vector<T, A>::capacity() const
{
    return capacity_;
}

template<typename T, typename A>
const T *Vector<T, A>::begin() const noexcept
{
    return data_;
}

template<typename T, typename A>
T *Vector<T, A>::begin() noexcept
{
    return data_;
}

template<typename T, typename A>
const T *Vector<T, A>::end() const noexcept
{
    return (data_ + size());
}

template<typename T, typename A>
T *Vector<T, A>::end() noexcept
{
    return (data_ + size());
}

template<typename T, typename A>
const T &Vector<T, A>::back() const
{
    return (data_[size() - 1]);
}

template<typename T, typename A>
T &Vector<T, A>::back()
{
    return (data_[size() - 1]);
}

template<typename T, typename A>
bool Vector<T, A>::empty() const
{
    return (size() == 0);
}

template<typename T, typename A>
const T *Vector<T, A>::data() const noexcept
{
    return data_;
}

template<typename T, typename A>
T *Vector<T, A>::data() noexcept
{
    return data_;
}

template<typename T, typename A>
void Vector<T, A>::reserve(size_t new_capacity)
{
    if (new_capacity > capacity()) {
        reallocate(new_capacity);
        capacity_ = new_capacity;
    }
}

template<typename T, typename A>
void Vector<T, A>::pop_back()
{
    if (size() > 0) {
        allocator_.Destroy(end() - 1);
        --size_;
    }
}

template<typename T, typename A>
void Vector<T, A>::operator=(Vector<T, A> &&other)
{
    std::swap(size_, other.size_);
    std::swap(capacity_, other.capacity_);
    std::swap(data_, other.data_);
    other.clear();
}

template<class T, class Allocator>
Vector<T> &Vector<T, Allocator>::operator=(const Vector<T> &other) {
  size_ = other.size();
  capacity_ = other.capacity();
  this->resize(other.size());
  std::copy(other.begin(), other.end(), data());
  return *this;
}

template<typename T, typename A>
void Vector<T, A>::resize(size_t count)
{
    resize(count, T());
}

template<typename T, typename A>
void Vector<T, A>::resize(size_t count, const T &value)
{
    if (count > capacity()) {
        reserve(count);
        for (size_t i = size(); i < count; ++i) {
            allocator_.Construct(data() + 1, value);
        }
    }
    else if (count < size()) {
        for (size_t i = count; i < size(); ++i) {
            allocator_.Destroy(data() + 1);
        }
    }
    size_ = count;
}

template<typename T, typename A>
template<typename... Args>
void Vector<T, A>::emplace_back(T &&arg, Args &&...args)
{
    if (capacity() == size()) {
        reallocate(capacity() + 1);
    }
    allocator_.Construct(end(), arg);
    ++size_;
    if constexpr (sizeof...(args) > 0) {
        emplace_back(args...);
    }
}

template<class T, class A>
bool Vector<T, A>::operator==(const Vector<T, A> &rhs) const
{
    if (this->size() != rhs.size()) {
        return false;
    }
    for (size_t i = 0; i < this->size(); ++i) {
        if (this->at(i) != rhs.at(i)) {
            return false;
        }
    }
    return true;
}

template<class T, class A>
bool Vector<T, A>::operator!=(const Vector<T, A> &rhs) const
{
    return !(*this == rhs);
}

template<class T, class A>
T &Vector<T, A>::operator[](size_t pos)
{
    if(pos >= this->size()) {
        throw std::range_error("");
    }
    return data_[pos];
}

template<class T, class Allocator>
const T &Vector<T, Allocator>::operator[](size_t pos) const {
    if(pos >= this->size()) {
        throw std::range_error("");
    }
    return data_[pos];
}

template<class T, class A>
void Vector<T, A>::clear()
{
    for (size_t i = 0; i < size(); i++) {
        allocator_.Destroy(data() + i);
    }
    size_ = 0;
}

template<class T, class Allocator>
T &Vector<T, Allocator>::at(size_t pos)
{
    if (pos < size()) {
        return data_[pos];
    }
    else {
        throw std::out_of_range("position is out of range");
    }
}

template<class T, class Allocator>
const T &Vector<T, Allocator>::at(size_t pos) const
{
    if (pos < size()) {
        return data_[pos];
    }
    else {
        throw std::out_of_range("position is out of range");
    }
}

template<class T, class A>
bool Vector<T, A>::operator>=(const Vector<T, A> &rhs) const
{
    if(this->size() < rhs.size()) {
        return false;
    }
    else if(this->size() > rhs.size()) {
        return true;
    }
    else {
        for(size_t i = 0 ; i < this->size() ; ++i) {
            if(this->at(i) < rhs.at(i)) {
                return false;
            }
        }
        return true;
    }
}

template<class T, class A>
bool Vector<T, A>::operator<=(const Vector<T, A> &rhs) const
{
    if(this->size() < rhs.size()) {
        return true;
    }
    else if(this->size() > rhs.size()) {
        return false;
    }
    else {
        for(size_t i = 0 ; i < this->size() ; ++i) {
            if(this->at(i) > rhs.at(i)) {
                return false;
            }
        }
        return true;
    }
}

template<class T, class A>
bool Vector<T, A>::operator>(const Vector<T, A> &rhs) const
{
    return !(*this <= rhs);
}

template<class T, class A>
bool Vector<T, A>::operator<(const Vector<T, A> &rhs) const
{
    return !(*this >= rhs);
}

矢量.h

#ifndef VECTOR_HPP
#define VECTOR_HPP
#include "../include/Allocator.hpp"
#include <iostream>
#include <limits>

template<class T, class A = Allocator<T>>
class Vector
{
public:
    Vector();
    Vector(size_t size, const T &value = T());
    Vector(const Vector &other);
    Vector(Vector &&other) noexcept;
    Vector(const std::initializer_list<T> &init);
    virtual ~Vector() noexcept;
    Vector<T> &operator=(const Vector<T> &other);
    void operator=(Vector<T, A> &&other);
    size_t CalculateCapacity(size_t newSize) const;
    void reallocate(size_t minSize);

    T &at(size_t pos);
    const T &at(size_t pos) const;
    T &operator[](size_t pos);
    const T &operator[](size_t pos) const;
    T &back();
    const T &back() const;
    T *begin() noexcept;
    const T *begin() const noexcept;
    T *end() noexcept;
    const T *end() const noexcept;
    T *data() noexcept;
    const T *data() const noexcept;
    bool operator!=(const Vector<T, A> &rhs) const;
    bool operator==(const Vector<T, A> &rhs) const;
    bool operator>=(const Vector<T, A> &rhs) const;
    bool operator<=(const Vector<T, A> &rhs) const;
    bool operator>(const Vector<T, A> &rhs) const;
    bool operator<(const Vector<T, A> &rhs) const;

    void pop_back();
    bool empty() const;
    size_t size() const;
    size_t capacity() const;
    void reserve(size_t new_cap);
    void clear();
    void push_back(T &item);
    void push_back(T &&item);
    template<class... Args>
    void emplace_back(T &&arg, Args &&...args);
    void resize(size_t count);
    void resize(size_t count, const T &value);

private:
    size_t size_ = 0;
    size_t capacity_ = 0;
    T *data_;
    Allocator<T> allocator_;
};

#include "../src/Vector.cpp"

#endif//VECTOR_HPP

分配器.cpp

#include "../include/Allocator.hpp"
template<typename T>
T *Allocator<T>::Allocate(size_t n)
{
    return (T *) (::operator new(n * sizeof(T)));
}

template<typename T>
void Allocator<T>::Deallocate(T *p, size_t n)
{
    delete (p);
}

template<typename T>
template<typename... Args>
void Allocator<T>::Construct(T *p, Args &&...args)
{
    new (p) T(args...);
}

template<typename T>
void Allocator<T>::Destroy(T *p)
{
    p->~T();
}

分配器.hpp

#ifndef ALLOCATOR_HPP
#define ALLOCATOR_HPP

#include <iostream>

template<typename T>
class Allocator
{
private:
    T *pointer;

public:
    T *Allocate(size_t n);
    void Deallocate(T *p, size_t n);
    template<typename... Args>
    void Construct(T *p, Args &&...args);
    void Destroy(T *p);
};

#include "../src/Allocator.cpp"

#endif//ALLOCATOR_HPP
c++ object hex
1个回答
0
投票

问题出在这个构造函数中。当分配内存容量=0时,此内存以后不再释放 模板

Vector<T, A>::Vector() : size_(0), capacity_(0)
{
    data_ = allocator_.Allocate(capacity());
}
© www.soinside.com 2019 - 2024. All rights reserved.