提问人:Aleksandr Dots 提问时间:10/5/2023 最后编辑:Aleksandr Dots 更新时间:10/6/2023 访问量:91
释放内存的问题(对于包含自定义向量的类)
Problem with freeing up memory (for a class containing a custom vector)
问:
#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 对象是由 copy 构造函数创建的,则内存会正常释放,因此 6 个 allocs ,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
答:
0赞
Aleksandr Dots
10/6/2023
#1
问题出在这个构造函数上。分配内存容量 = 0 时,将来不会释放此内存 模板<T类,A类>
Vector<T, A>::Vector() : size_(0), capacity_(0)
{
data_ = allocator_.Allocate(capacity());
}
评论
this->hex.~Vector();
--你为什么要这么做?为什么要显式调用成员变量的析构函数?Vector
Vector
Hex
Hex
Vector
new
Vector