I'm experimenting the usage of placement new to try to understand how it works.
Executing the code below:
#include <iostream>
#define SHOW(x) std::cout << #x ": " << x << '\n'
template<typename T>
static T *my_realloc(T *ptr, size_t count) {
return (T *) realloc((void *) ptr, count * sizeof(T));
}
template<typename T>
static void my_free(T *ptr) {
free((T *) ptr);
}
int main() {
constexpr int count = 40;
int cap = 0;
int size = 0;
std::string *strs = nullptr;
auto tmp_str = std::string();
for(int i = 0; i < count; i++) {
tmp_str = std::to_string(i);
if(size == cap) {
if(cap == 0)
cap = 1;
else
cap *= 2;
strs = my_realloc(strs, cap);
}
new (&strs[size++]) std::string(std::move(tmp_str));
}
for(int i = 0; i < size; i++)
SHOW(strs[i]);
std::destroy_n(strs, size);
my_free(strs);
return 0;
}
I get the errors:
Invalid read of size 1
Invalid free() / delete / delete[] / realloc()
Removing the line
std::destroy_n(strs, size);
The error of invalid free is solved, but somehow all memory of the program is freed and no leaks are generated. But i can't find how the std::string destructor is called in the program.
If you want to store non-trivial types (such as std::string), then realloc simply cannot be used. You will find that standard library containers like e.g. std::vector will also not use it.
realloc may either extend the current allocation, without moving it in memory, or it might internally make a new allocation in separate memory and copy the contents of the old allocation to the new one. This step is performed as if by std::memcpy. The problem here is that std::memcpy will only work to actually create new objects implicitly in the new allocation and copy the values correctly if the type is trivially-copyable and if it and all of its subobjects are implicit-lifetime types. This definitively doesn't apply to std::string or any other type that manages some (memory) resource.
You are also forgetting to check the return value of realloc. If allocation failed, it may return a null pointer, which you will then use regardless, causing a null pointer dereference, as well as a memory leak of the old allocation.
Instead of using realloc you should make a new allocation for the new size, then placement-new copies of the objects already in the old allocation into the new one and then destroy the objects in the old allocation and free it.
If you want to guarantee that there won't be memory leaks when exceptions are thrown things become somewhat complicated. I suggest you look at the std::vector implementation of one of the standard library implementations if you want to figure out the details.
strs = my_realloc(strs, cap);
strs is a pointer to a std::string, and this will result in the contents of the pointer to be realloc()ed.
This is undefined behavior. C++ objects cannot be malloced, realloced, or freeed. Using a wrapper function, or placement new, at some point along the way, does not change that.
Everything from this point on is undefined behavior, and the resulting consequences are of no importance.
Related
I'm having trouble allocating memory on the heap for an array of strings. Allocating with new works but malloc segfaults each time. The reason I want to use malloc in the first place is that I don't want to call the constructor unnecessarily.
This works fine
std::string* strings = new std::string[6];
This doesn't
std::string* strings = (std::string *)malloc(sizeof(std::string[6]));
One thing I've noticed is that the first variant (using new) allocates 248 bytes of memory while the second allocates only 240. This 8 byte difference is constant no matter the size of the array from what I've gathered and I cannot find what the source of the difference is.
Here's the whole code that segfaults.
#include <iostream>
void* operator new(size_t size)
{
std::cout << size << std::endl;
return malloc(size);
}
void* operator new [](size_t size)
{
std::cout << size << std::endl;
return malloc(size);
}
int main() {
std::string* strings = new std::string[6];
strings = (std::string *)malloc(sizeof(std::string[6]));
strings[0] = std::string("test");
return 0;
}
Another thing I've noticed is that the above code seems to work if I use memset after malloc to set all of the bytes I allocated with malloc to 0. I don't understand where the 8 byte difference comes from if this works and also why this variant works at all. Why would it work just because I set all of the bytes to 0?
malloc() only allocates raw memory, but it does not construct any objects inside of that memory.
new and new[] both allocate memory and construct objects.
If you really want to use malloc() to create an array of C++ objects (which you really SHOULD NOT do!), then you will have to call the object constructors yourself using placement-new, and also call the object destructors yourself before freeing the memory, eg:
std::string* strings = static_cast<std::string*>(
malloc(sizeof(std::string) * 6)
);
for(int i = 0; i < 6; ++i) {
new (&strings[i]) std::string;
}
...
for(int i = 0; i < 6; ++i) {
strings[i].~std::string();
}
free(strings);
In C++11 and C++14, you should use std::aligned_storage to help calculate the necessary size of the array memory, eg:
using string_storage = std::aligned_storage<sizeof(std::string), alignof(std::string)>::type;
void *buffer = malloc(sizeof(string_storage) * 6);
std::string* strings = reinterpret_cast<std::string*>(buffer);
for(int i = 0; i < 6; ++i) {
new (&strings[i]) std::string;
}
...
for(int i = 0; i < 6; ++i) {
strings[i].~std::string();
}
free(buffer);
In C++17 and later, you should use std::aligned_alloc() instead of malloc() directly, eg:
std::string* strings = static_cast<std::string*>(
std::aligned_alloc(alignof(std::string), sizeof(std::string) * 6)
);
for(int i = 0; i < 6; ++i) {
new (&strings[i]) std::string;
}
...
for(int i = 0; i < 6; ++i) {
strings[i].~std::string();
}
std::free(strings);
Allocating via new means the constructor is run. Please always use new and delete with C++ classes (and std::string is a C++ class), whenever you can.
When you do malloc() / free(), only memory allocation is done, constructor (destructor) is not run. This means, the object is not initialized. Technically you might still be able to use placement new (i.e., new(pointer) Type) to initialize it, but it's better and more conformant to use classic new.
If you wanted to allocate multiple objects, that's what containers are for. Please use them. Multiple top-grade engineers work on std::vector<>, std::array<>, std::set<>, std::map<> to work and be optimal - it's very hard to beat them in performance, stability or other metrics and, even if you do, the next coder at the same company needs to learn into your specific data structures. So it's suggested not to use custom and locally implemented allocations where a container could be used, unless for a very strong reason (or, of course, didactic purposes).
I want to implement 2 array addition, but when a destructor to the class SList
void operator+(SList list2) {
int totalLen = this->len + list2.len;
char** temp = new char* [totalLen];
for(int i = 0; i < len; i++) {
temp[i] = this->list[i];
}
for(int i = len, j = 0; i < totalLen; i++, j++) {
temp[i] = list2.get(j);
}
delete[] this->list;
this->list = temp;
this->len = totalLen;
cout << len << endl << endl;
}
Here are the get method that just return the dynamic array of char:
char* get(int i) {
if (i >= len) {
return "";
} else {
return list[i];
}
}
here are my class SList private variables:
private:
char** list;
int len;
char* generateString(){
char* str;
int n = rand() % 20 + 1;
str = new char[n + 1];
for(int i = 0; i < n; i++) {
str[i] = 'a' + rand()%26;
}
str[n] = '\0';
return str;
};
~SList() {
delete[] list;
}
It always shows malloc error on the destructor.
malloc: *** error for object 0x105007410: pointer being freed was not allocated
malloc: *** set a breakpoint in malloc_error_break to debug
Please help! I have carefully checked my delete method on the dynamic allocated array, but it always shows this error.
I have tried to check other delete from the other function, but none of them make the same malloc error. I have tried to commented the destructor method and everything work fine. But i really need to have destructor method here. I hope someone with more expertise on c++ can help me fix this malloc error and gave an explanation on which part i made a mistake.
Regardless what are other details of implementation, the destructor is not correct while you're using a data structure known as a "ragged array", i.e. list is a pointer to an array of pointers. delete[] would free the pointer array, but not char arrays pointed by its elements. You have to do something like this:
~SList() {
if(!list) return; // if we cannot guarantee that list isn't
// nullptr we have to check it,
// or result of list[i] would be undefined.
for(int i = 0; i < len; i++)
delete[] list[i];
delete[] list;
}
and you have to make sure that any of those pointers is either initialized by new expression or is equal nullptr.. It doesn't happen on its own. You have to make sure during construction and all operations. You didn't show any. Look for faults there.
The method get() is a disaster waiting to happen and is ill-formed, i.e. it doesn't follow C++ rules. The string literal "" returns const char*, always the same one and the statement return ""; is incorrect - some compilers only warn about it though. It cannot be deallocated by delete.
char* get(int i) {
if (i >= len) {
return nullptr; // "" - is not safe
} else {
return list[i];
}
}
Deleting a nullptr is a safe no-op. Deleting something that was't returned by new is a disaster.
The addition operator is taking list2 by value, which means that proper copy operations have to be implemented. You didn't show them either. Default implementation would just copy a pointer and destruction of local copy would deallocate memory used by original via ~SList() above. The operator have to return resulting object and should not modify one pointed by this. You had implemented an operator+=. The way you did it, it would work as
a+b; // a now contains result of concatenation.
It's simply weird to use. Proper operator would be
SList operator+(SList& list2);
In general, an object that deals with ownership of some resource, dynamic memory in our case, have to implement certain set of special member functions:
~SList();
SList(const SList& other);
SList(SList&& other); // move copy, source is a temporal.
SList& operator=(const SList& other);
SList& operator=(SList&& other); // move assignment
If that is done right, you can safely do the c = a + b; assignment.
Note that if you pass argument by reference you have to take in account that arguments of assigning operators aren't referencing the object pointed by this and act accordingly if they are. Otherwise you would destroy it and loose original data. On other hand copying argument is excessive and user-unfriendly due to increased cost and memory footprint. Concatenation of n-element and m-element array is expected to have memory footprint of n+m elements , not n+2*m.
I have a class with private member variable
shared_ptr<short> m_p_data;
I get heap corruption when I use this constructor:
Volume2D(const int dimX, const int dimY) :m_dimX{ dimX }, m_dimY{ dimY }, m_p_data{ make_shared<short>(dimX*dimY) } {
}
but there is no heap corruption if I do this instead:
Volume2D(const int dimX, const int dimY) :m_dimX(dimX), m_dimY(dimY) {
m_p_data.reset(new short[dimX*dimY]);
}
To be more specific, here is the code that corrupts the heap:
Volume2D vol(10, 1);
for (auto i = 0; i < 10; ++i) {
vol(i, 0) = i;
cout << "value = " << vol(i, 0) << endl;
}
return 0;
Both versions of your code are problematic.
The first version,
make_shared<short>(dimX*dimY)
creates a single heap-allocated short with the value dimX*dimY. It is apparent from the rest of your question, that your code later treats this logically as an array of dimension dimX*dimY, which is exactly what's causing the heap corruption (you only allocated a single short, but you're treating it like many).
The second version has the opposite problem. You're allocating dimX*dimY shorts, but, as far as your shared_ptr, it doesn't know that. So it doesn't have your heap corruption, but the shared_ptr destructor calls delete, not delete[] (even though you allocated with new[], not new).
For this case, its' unclear why you need a shared_ptr to begin with. Why not use std::vector<short>, or std::vector<std::vector<short>>?
I want to allocate memory for n T objects by allocating n * sizeof(T) bytes and get a pointer using new. What's wrong in the following code?
template <class T>
void foo(size_t n, const T& value)
{
T* buffer = reinterpret_cast<T*>(new char[n * sizeof(T)]);
for (int i = 0; i < n; i++) {
buffer[i] = value;
std::cout << buffer[i] << std::endl;
}
std::cout << "success" << std::endl;
}
If i do foo<int>(n, int());, but if I call foo<std::string>(n, std::string()); i get segmentation fault.
You need to create an object in the memory, not just reinterpret the uninitialised bytes as an object. For int, you'll get away with it, since assignment will just overwrite whatever's there. std::string will (probably) contain an invalid pointer and size, and will think that these refer to data that it's supposed to delete or replace with the assigned value; hence the crash.
To create an object in pre-allocated memory, use placement-new instead of assignment:
new(&buffer[i]) T(value);
When you've finished with an object, you'll have to call its destructor yourself before deallocating the memory:
buffer[i].~T();
You could avoid this error-prone dance with std::vector<T> buffer(n, value); which does all this behind the scenes without troubling you about it.
I'm going to write a piece of code (Function) that returns a pointer to an array.
but I don't know how to do that.
The code I wrote is :
int* prime_factor(int temp){
int ctr;
int *ret;
int i = 0;
while (temp != 1){
ctr = 2;
if (temp%ctr != 0){
ctr++;
}
else {
*(ret + i) = ctr;
temp /= ctr;
}
}
return ret;
}
I guess that there's a need to such the thing :
else {
ret = new int[1];
*(ret +i) = ctr;
temp /= ctr;
}
But as you know , implementation of this stuff needs to be deleted the memory that you have allocated , so we have to delete the memory outside of the function , so it going to be nonstandard function.
Indeed, i want to calculate the Prime factors of a number then return them out.
Any idea to do that ? I don't know what should I do to gain the goal.
thank you so much.
I see your question has also the tag C++ so, you could use C++. I don't really know what you mean with without static integer ....
Use vector.
#include <vector>
vector<int> prime_factor(int number)
{
int ctr = 2;
vector<int> factors;
while (number != 1)
{
if (number % ctr != 0)
ctr++;
else
{
factors.push_back(ctr);
number /= ctr;
}
}
return factors;
}
As you will be using the vector with integers it knows how to destroy ("delete") it self.
Example:
int main()
{
for (auto &x : prime_factor(20)) // C++11
{
cout << x << endl;
}
}
Output:
2
2
5
Yes, putting burden of deleting the array on user is not preferable. Here you would be better off using vector instead of plain array.
You can also pass array as an argument(created in the caller so that it would be more intuitive to use delete on that array) to this function.
The line *(ret + i) = ctr; will create a memory violation right away, since your ret pointer has no allocated memory pointing to. You nee do preallocate a memory for it, either statically (array declaration) in the code CALLING this function, and passing it to the function as a parameter, or dynamically (using malloc or similar) and then freeing it in some point. Dynamic allocation can be done either in the function itself or in the calling code. But again, free the memory afterwards.
int *ret;
In your case ret doesn't point to any memory location.You are trying to reference some unallocated memory location which might cause segmentation fault.
Just to show how to return a pointer and free it below is the example:
int *some()
{
int *ret = malloc(sizeof(int));
*ret = 10;
return ret;
}
int main()
{
int *p = some();
printf("%d\n",*p);
free(p);
}
"But as you know , implementation of this stuff needs to be deleted the memory that you have allocated , so we have to delete the memory outside of the function , so it going to be nonstandard function."
To avoid that dilemma c++ has introduced Dynamic memory management support.
Instead of using raw pointers like int* use one appropriate of
std::unique_ptr<int> (to transfer ownership)
std::shared_ptr<int> (to share ownership)
std::weak_ptr<int> (to share ownership, but not count as reference)
std::auto_ptr<int> (to transfer ownership "the old way")
Besides that, it looks like using a std::vector<int> would be much more appropriate, than using a simple int*. The std::vector<int> class also is purposed to release you from getting the dynamic memory management right on your own.