c++ - array of shared_ptr - c++

Context:
I try to avoid vectors and replace it with smart pointers as an exercise.
The goal is to benefit from smart pointers to avoid memory leaks without relying on vectors, because that is what I want to try now.
The codes below is just a mean to be easily understood.
I want my c++ code to work.
Update : I would like to stick as much as possible with raw c'ish style for IO: please no std::string or c++ streams in general.
The codes:
C version (working):
typedef struct{
char ** my_arr;
} MyInputs;
...
MyInputs *Doc = malloc(sizeof *Doc);
*Doc->my_arr = malloc(sizeof (*Doc->my_arr) * 2);
Doc->my_arr[0] = malloc(10);
Doc->my_arr[1] = malloc(10);
// Yes, that is stupid to alloc 10 bytes and use only 6 or 5. That is for the example.
memcpy(Doc->my_arr[0],(char*)"Hello\0",6);
memcpy(Doc->my_arr[1],(char*)"Cool\0",5);
printf("%s %s \n",Doc->my_arr[0],Doc->my_arr[1] );
...
C++ version (attempt):
typedef struct {
std::shared_ptr<char*>my_arr;
}MyInputs;
...
std::shared_ptr<MyInputs> MainDoc (static_cast<MyInputs*>(malloc(sizeof (*MainDoc))),free);
std::shared_ptr<char*> Z (static_cast<char**>(malloc(sizeof (**MainDoc->my_arr) * 10)),free);
std::shared_ptr<char> Z[0](static_cast<char*>(malloc(sizeof (char *) * 10)),free);
memcpy(Z[0].get(), (char*)"salut\0", 6);
cout << Z[0] << endl;
...
The obvious:
In the c++ version, the compiler complains about Z and Z[0] being the same and that there is not match for operator [] in the cout.
Well, well, well...
Any ideas that could make the c++ code work that way ? (and again, I know about vectors).

If you insist on using the wrong tool for the job, then you'll want each inner pointer to also be a smart pointer:
shared_ptr<shared_ptr<char>> my_arr;
You can't simply use malloc to create an array of non-trivial types like shared_ptr; use new:
my_arr.reset(new shared_ptr<char>[10], [](shared_ptr<char> * p) {delete [] p;});
Then you can assign or reset the elements. shared_ptr doesn't support [], since it's generally not used for arrays, so use get() to get a pointer to the first element:
my_arr.get()[0].reset(new char[10], [](char * p) {delete [] p;});
Or just replace this gibberish with something sensible
std::vector<std::string> my_arr {"Hello", "Cool"};

Related

Casting SomeType** to SomeType*[] and vice versa

I really need to this specifically as I am using SWIG and need to make a cast to match the function definition.
The function definition accepts
SomeType const * const variable_name[]
Also, another question would be-
How to allocate memory to
SomeType * variable[] = <??? using new or malloc >
for x entries?
Edit:
I have searched quite a lot, but I keep stumbling into post which allocate memory to SomeType** using new SomeType*[x] i.e.
SomeType** variable = new SomeType*[x];
Can you please tell me a way to do this?
The function wants an array of pointers.
The statement:
SomeType * variable[];
Is not valid syntax.
You will need:
SomeType * * variable;
Declares a pointer to a pointer of SomeType.
You will need to perform memory allocation in two steps.
First, allocate the array of pointers:
variable = new SomeType * [/* some quantity */];
Remember, the above statement only allocates room for the pointers. The memory contents is still not initialized.
Secondly, allocate pointers to the objects.
for (unsigned int i = 0; i < some_quantity; ++i)
{
variable[i] = new SomeType;
}
When deleting, delete the contents of the array before the array:
for (unsigned int i = 0; i < some_quantity; ++i)
{
delete variable[i];
}
delete[] variable;
The function definition accepts
SomeType const * const variable_name[]
I'm no expert in C++, but if the declaration of arrays in function parameters is the same as in C then this is a synonym for the following type:
SomeType const * const * variable_name
That is, an array in a function parameter is really a pointer. Your book should have explained this early on.
I have searched quite a lot, but I keep stumbling into post which allocate memory to SomeType** using new SomeType*[x] i.e.
SomeType** variable = new SomeType*[x];
Can you please tell me a way to do this?
You could indeed allocate a SomeType const * const * using similar code to that, so you've answered your own question. I assume you could also use an std::vector<SomeType const * const> like so:
std::vector<SomeType const * const> *foo = new std::vector<SomeType const * const>();
/* XXX: Add some items to the vector */
SomeType const * const *bar = &foo[0];
This would be useful if you're not sure how many items foo should contain, and you expect it to grow.
I don't understand why people don't read books anymore. It's the fastest and cheapest (if you consider the cost of man hours) way to learn correctly from a reputable figure.

How do I do a memset for a pointer to an array?

How do I do a memset for a pointer to an array?
int (*p)[2];
p=(int(*))malloc(sizeof(*p)*100);
memset(p,0,sizeof(*p)*100);
Is this allocation an correct?
you can use calloc.
calloc will replace both malloc and memset.
p = calloc(100, sizeof (*p));
I'll summarize a lot of answers (although I've ignored some of the stylistic variants in favor of my own preferences).
In C:
How to use malloc:
int (*p)[2] = malloc(100 * sizeof(*p));
How to use memset:
memset(p, 0, 100 * sizeof(*p));
How to do both in one statement:
int (*p)[2] = calloc(100, sizeof(*p));
In C++, the same is possible except that you need to cast the results of malloc and calloc: static_cast<int(*)[2]>(std::malloc(100 * sizeof(*p)).
However, C++ provides alternative ways to allocate this:
int (*p)[2] = new int[100][2](); // like calloc.
delete[] p; // *not* delete p
C++ also provides vector, which is usually nice, but unfortunately you cannot create a vector of C-style arrays. In C++03 you can workaround like this:
struct my_array {
int data[2];
};
std::vector<my_array> p(100);
// no need to free/delete anything
I don't think that zeros the elements, although I might be wrong. If I'm right, then to zero you need:
my_array initvalue = {0};
std::vector<my_array> p(100, initvalue);
another way to represent 2 ints:
std::vector<std::pair<int,int> > p(100);
If you can use Boost:
std::vector<boost::array<int, 2> > p(100);
In C++11:
std::vector<std::array<int, 2>> p(100);
I've listed these in increasing order of how good they usually are, so use the last one that isn't blocked by whatever constraints you're working under. For example, if you expect to take a pointer to the first element of one of the inner arrays-of-2-int, and increment it to get a pointer to the second, then std::pair is out because it doesn't guarantee that works.
The elegant way:
typedef int int_arr_2[2];
int_arr_2* p;
p = malloc(sizeof(int_arr_2)*100);
memset(p,0,sizeof(int_arr_2)*100);
The best way:
typedef int int_arr_2[2];
int_arr_2* p;
p = calloc(100, sizeof(int_arr_2));
calloc, unlike malloc, guarantees that all bytes are set to zero.
The memset() line is proper.
For C you don't need malloc casting.
In C++ if you still want to do this, the type cast should be as:
p = (int(*)[2]) malloc(sizeof(*p)*100); // or `static_cast<...>
// ^^^^^^^^^
But I would suggest to change your approach for using std::vector instead. Cleaner, better and "semi-automatic":
std::vector<int*> vi(100); // or std::vector vi(100, nullptr);
Another way with raw pointers is to use new[]:
int **p = new[100](); // allocates and sets to 0
But you have to manage this memory later on by deallocating with delete[]
In C (not C++) you would just do
int (*p)[2] = malloc(sizeof(*p)*100);
memset(*p,0,sizeof(*p)*100);
that is, variables can be initialized with expressions and malloc doesn't need (and should not have) a cast. Then *p is an "lvalue" of your array type that decays to a pointer when passed to memset.

How to make it so the user can't delete a dynamic array?

In writing a response, I wrote some code that challenged my assumptions on how const pointers work. I had assumed const pointers could not be deleted by the delete function, but as you'll see from the code below, that isn't the case:
#include <new>
#include <string.h>
class TestA
{
private:
char *Array;
public:
TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
~TestA(){if(Array != NULL){ delete [] Array;} }
char * const GetArray(){ return Array; }
};
int main()
{
TestA Temp;
printf("%s\n",Temp.GetArray());
Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
Temp.GetArray()[1] = ' ';
printf("%s\n",Temp.GetArray());
//Temp.GetArray() = NULL //This doesn't work
delete [] Temp.GetArray(); //This works?! How do I prevent this?
}
My question is, how do I pass the user access to a pointer (so they can use it like a char array), whilst making it so that the delete function cannot delete it, by preferably throwing some sort of complaint or exception?
If your users are using delete[] on pointers they didn't get from new[], hit them upside the head with a clue bat.
There are so many reasons a pointer can be dereferenced but mustn't be passed to delete:
Someone else will delete it.
It's not the beginning of the block.
It came from malloc or some other non-new allocator.
It has static, not dynamic lifetime.
If has automatic lifetime.
Some of these will manifest in exceptions at runtime. Others will cause crashes at some later time. All are "undefined behavior" according to the standard.
The assumption should be that a pointer cannot be used as the argument to delete, unless explicitly stated otherwise.
If entry-level programmers are making this mistake, educate them. If "experienced" developers are doing it, fire them for lying on their resume.
If you just want it to hurt when they do that, allocate the array one larger than necessary, and return Array + 1;. Now everything will blow up if they try to use delete[].
The practical use is that it's (more) likely to make the program crash inside the bogus call to delete, with the offending function still on the call stack. Where the original will probably continue running for a while, and finally crash in innocent code. So this helps you catch stupid users.
delete [] Temp.GetArray(); //This works?! How do I prevent this?
As long as, it returns char* or some other pointer type, you cannot prevent it; it doesn't matter if the expression in delete statement is const, because all of the following are perfectly valid in C++:
char *pc = f();
delete [] pc; //ok
const char *pc = g();
delete [] pc; //ok
char * const pc = h();
delete [] pc; //ok
const char * const pc = k();
delete [] pc; //ok
However, if you change this :
char *Array;
to this
std::vector<char> Array;
And then you could achieve what you want, by returning it as:
std::vector<char> & GetArray() { return Array; }
The bottomline is :
In C++, the default choice for dynamic array should be std::vector<T> unless you've a very strong reason not to use it.
Your const is doing no effect, you are returning a const pointer, not a pointer to const char, which is what you want. Also see Nawaz recommendation on using vectors instead.
The most you can really do is to return something that is not an acceptable operand for delete. This can be an RAII object (like std::vector or std::array) or it can be a reference (depending on your situation, either could be appropriate).
[Un]fortunately, C++ lets the programmer do all sorts of sneaky things.
You cannot block stupidity entirely, but you can stifle it a tad by using an access mechanism instead of returning the actual array pointer.
char& operator[](size_t index) { return Array[index]; }
This does not address the ability to treat it like a char array, but as has been pointed out, if you reveal that pointer, (bad) programmers a free to run delete on it all they want.

How to initialize c-strings array (no stl)

I want to initialize array of c-strings with zero pointers in MSVC2010
// Foo.h
#pragma once
class Foo {
int sz_;
char **arr_;
public:
Foo();
~Foo();
// ... some other functions
};
// Foo.cpp
#include "Foo.h"
#define INITIAL_SZ 20
Foo::Foo() : sz_(INITIAL_SZ) {
// there I have to initialize arr_ (dynamic array and can be enlarged later)
arr_ = (char **)calloc(INITIAL_SZ * sizeof (char *)); // ???
// or maybe arr_ = new ...
}
How to correct initialize arr_? I was not allowed to use of STL, MFC, etc.
arr = new char*[INITIAL_SZ](); will do - you can even put it in an initialization list.
If you really want to avoid STL, etc., then why not:
arr_ = new char*[INITIAL_SZ]();
You could even put this in the initializer list.
Remember to invoke delete [] arr_ in your destructor. (As #Nawaz points out below, you should probably also follow the Rule of Three, and define a suitable copy-constructor and assignment operator as well.)
1. Build a proper string class
2. Build a proper array class
3. Use the array on strings
Happy chasing memory leaks, double frees and memory corruption.
arr_ = (char **)calloc(INITIAL_SZ * sizeof (char *));
should be
arr_ = (char **)calloc(INITIAL_SZ, sizeof (char *));
The correct way is to redefine arr_ as std::vector<std::string> and to use vector::reserve() to hint at the number of strings you expect to have. Let C++ take care of the memory for you.
But if you must use raw C strings, you probably want:
arr_ = new char *[sz_];

Trouble with char* and char** (C --> C++)

Okay, I am trying to integrate some C code into a C++ project, and have run into a few problems. I will detail the first one here.
I keep running into this error:
error: cannot convert 'char*' to 'char**' in assignment|
here is the offending code (with the breakpoint marked):
char** space_getFactionPlanet( int *nplanets, int *factions, int nfactions )
{
int i,j,k;
Planet* planet;
char **tmp;
int ntmp;
int mtmp;
ntmp = 0;
mtmp = CHUNK_SIZE;
tmp = malloc(sizeof(char*) * mtmp); <--- Breakpt
The malloc function is derived from a C header. Here is the declaration:
_CRTIMP void* __cdecl __MINGW_NOTHROW malloc (size_t) __MINGW_ATTRIB_MALLOC;
I am using codeblocks, which is set to use MinGW. The above syntax is totally foreign to me.
I am totally stumped, since this code works fine in the C program I took it from.
Any Ideas?
EDIT 1:
Oops, just realized that the declaration is from stdlib.h.
EDIT 2:
I tried:
tmp = static_cast<char **>(malloc(sizeof(char*) * mtmp));
As suggested, but not I get error: invalid static_cast from type 'char*' to type 'char**'.
EDIT 3:
Okay, reinterpret_cast works, but the solution to replace mallocs seems much more elegantly simple, so I am going with that.
However, there is no free(tmp) at the end of the function. Is this a problem if I don't put in a delete tmp[]?
EDIT 4: I should add that tmp is returned by the function, so it is neccesary to delete tmp, or is this automatic?
Okay I am marking this solved. Thanks for your help.
C++ is not so free with pointer type conversions. You will have to do something like this:
tmp = static_cast<char **>(malloc(sizeof(char *) * mtmp));
That will work if your malloc() returns a void*. However, the errors you're getting indicate that your malloc() is declared to return a char*, and in this case you'd have to use reinterpret_cast instead:
tmp = reinterpret_cast<char **>(malloc(sizeof(char *) * mtmp));
This casts the return type of malloc() to a type suitable for assignment into tmp. You can read more about the different types of casts in C++ at Type Casting.
Note that if this code must still compile in C, you can use a C-style cast instead:
tmp = (char **)malloc(sizeof(char *) * mtmp);
If you're converting to C++, then let's do away with the malloc shall we?
tmp = new char*[mtmp];
But later on, you will probably find something like this:
free(tmp);
Which you need to change to this:
delete [] tmp;
If tmp is returned from the function, do not delete it. You need to trace the pointer. Somewhere, perhaps in multiple locations, free is going to be called. You need to replace that with delete if you are going with this solution. However, an even better solution would be to ditch the pointers all together, and replace it with a
vector<string>
malloc(3) returns void*, which could be assigned to any other pointer type in C, but not in C++. This is one of the places where C++ is not backward-compatible with C. You can either do what other posters said - cast it to char**, or go all the way to proper C++ free store usage with operators new[] and delete[]:
char** tmp = new char*[mtmp]; // was malloc(sizeof(char*) * mtmp);
// ... use tmp
delete [] tmp; // was free( tmp );
Perhaps a cast is in order:
tmp = static_cast<char **>(malloc(sizeof(char*) * mtmp));
Change tmp = malloc(sizeof(char*) * mtmp); to:
tmp = (char**)malloc(sizeof(char*) * mtmp);
And don't go around changing malloc's to new's, as some have wrongly suggested, because of two reasons:
1) its not broken, so don't fix it (!)
2) you could introduce subtle or flat out horrible bugs that will eat away hours of your life later on.
Do spend your time fixing broken code, not code that works...