Неправильно отображаются данные
Мой код:
#include <iostream>
#include <stdlib.h>
#include <exception>
#include <string>
class MemoryException : public std::exception{
public:
MemoryException(std::string message){
this->message = message;
}
const char* what() const noexcept override{
return message.c_str();
}
private:
std::string message;
};
class Memory{
public:
Memory(int size){
data = malloc(size);
if(data == nullptr){
throw MemoryException("Failed to allocate memory");
}
sizeMemory = size;
freeMemory = size;
}
~Memory(){
free(data);
delete[] headline;
}
template<typename T>
void set(T values){
const int size = sizeof(values);
if(size > freeMemory){
throw MemoryException("Object too bid " + std::to_string(size) + "/" + std::to_string(freeMemory));
}
set(values, sizeMemory - freeMemory);
addH(size);
freeMemory -= size;
}
template<typename T>
T get(int index){
if(index > quantity){
throw MemoryException("Went beyond the limits of memory");
}
return *getType<T>(getPosition(index));
}
int getFreeMemory(){
return freeMemory;
}
void remove(int index){
if(index > quantity){
throw MemoryException("Went beyond the limits of memory");
}
const int start = getPosition(index);
const int size = headline[index];
char* val = (char*)data;
val += start;
for(int i = 0; i < sizeMemory - start; i++){
*(val + i) = *(val + size + i);
}
int* h = new int[quantity - 1];
for(int i = 0; i < index; i++){
h[i] = headline[i];
}
for(int i = index; i < quantity - 1; i++){
h[i] = headline[i + 1];
}
delete[] headline;
headline = h;
quantity--;
freeMemory += size;
}
private:
int sizeMemory;
int freeMemory;
int quantity = 0;
int* headline = nullptr;
void* data;
template<typename T>
T* getType(int size){
char* f = static_cast<char*>(data) + size;
return static_cast<T*>(static_cast<void*>(f));
}
int getPosition(int index){
int s = 0;
for(int i = 0; i < index; i++){
s += headline[i];
}
return s;
}
template<typename T>
void set(T values, int position){
const int size = sizeof(values);
char* val = (char*)data;
val += position;
T* d = &values;
char* dI = (char*)d;
for(int i = 0; i < size; i++){
*(val + i) = *(dI + i);
}
}
void addH(int size){
int* h = new int[quantity + 1];
for(int i = 0; i < quantity; i++){
h[i] = headline[i];
}
h[quantity] = size;
delete[] headline;
headline = h;
quantity++;
}
};
struct Human{
std::string name;
int age;
std::string info(){
return "name: " + name + " age: " + std::to_string(age);
}
};
int main(){
Human h;
Memory a(80);
h.age = 28;
h.name = "Bob";
try{
a.set(h);
a.set(5);
a.set("is C++ code");
a.set(false);
std::cout << a.get<Human>(0).info() << std::endl;
std::cout << a.get<int>(1) << std::endl;
std::cout << a.get<const char*>(2) << std::endl;
std::cout << a.get<bool>(3) << std::endl;
std::cout << "---REMOVE---" << std::endl;
a.remove(1);
std::cout << a.get<Human>(0).info() << std::endl;
std::cout << a.get<const char*>(1) << std::endl;
std::cout << a.get<bool>(2) << std::endl;
}catch(const MemoryException& ex){
std::cout << ex.what() << std::endl;
}
}
Вывод:
name: Bob age: 28
5
is C++ code
0
---REMOVE---
name: 8j_ age: 28
is C++ code
0
после удаления 1-го элемента вместо Bob выводится непонятно что, а в остальных сучьях вывод корректный
Ответы (1 шт):
Я немного упростил ваш пример:
#include <cstring>
#include <string>
class Memory{
public:
void set(std::string s) { memcpy(data, &s, sizeof(s)); }
std::string get() {
return *static_cast<std::string*>(static_cast<void*>(data));
}
private:
char data[sizeof(std::string)];
};
int main(){
Memory a;
std::string name = "BobBobBobBobBobBobBobBobBobBobBob";
a.set(name);
auto s = a.get();
}
Скомпилировал его с отладочной информацией и запустил valgrind:
$ g++ -g temp.cpp $ valgrind ./a.out ==2407848== Memcheck, a memory error detector ==2407848== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==2407848== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info ==2407848== Command: ./a.out ==2407848== ==2407848== Invalid read of size 8 ==2407848== at 0x484B345: memmove (vg_replace_strmem.c:1382) ==2407848== by 0x49AC806: void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct<char*>(char*, char*, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30) ==2407848== by 0x1093B6: Memory::get[abi:cxx11]() (temp.cpp:10) ==2407848== by 0x1092CE: main (temp.cpp:21) ==2407848== Address 0x4d7bcf0 is 0 bytes inside a block of size 34 free'd ==2407848== at 0x484471B: operator delete(void*) (vg_replace_malloc.c:923) ==2407848== by 0x1092B8: main (temp.cpp:20) ...
valgrind жалуется что программа читает память, которую уже освободили. Чтобы понять почему посмотрим что делает вызов a.set(name);:
- создаёт копию строки
name. Строка передана в метод по значению, поэтому она копируется. - копирует байты копии строки в "память".
- уничтожает копию строки
name.
Копия строки содержит в себе указатель на массив символов. Когда копия уничтожается, массив освобождается. Но вы запомнили указатель на него в "памяти" и позже будете обращаться к освобождённому блоку памяти.
Важное дополнение: строка сделана длинной чтобы избежать Small string optimization (SSO). Это механизм при котором буфер под короткие строки не выделяется в свободной памяти а помещается прямо в тело объекта. SSO делает ошибку менее заметной, скажем так. Короткую строку можно скопировать и копия будет выглядеть для программы нормально.
Но в любом случае вы пытаетесь создать объект C++ без вызова конструктора. Есть условия при которых это возможно, но не в случае std::string. Все экземпляры std::string должны быть сконструированы должным образом. Копировать байты и рассчитывать что полученная копия будет работать нельзя.
Есть способ создавать объекты в "сырой" памяти. Почитайте про placement new.