#include <memory>
int main()
{
std::shared_ptr<double> array (new double [256], [](double * d){
delete [] d;
});
}
I made a shared_ptr pointing into an array of doubles which has its own custom deleter.
Now how can I access the array? Let's say I wish to access the array at index 1. I tried the usual "bracket method" but I get errors.
The word array points to its first element by default, but what if I want to access the 2nd element? Using increments and bracket gives me the "no match for operator" error.
Can someone explain to me what's happening under the hood?
I am asking this for research purposes, despite being aware that unique_ptr and vector will do a better job.
The bracket notation is defined to work with pointer types (and you're right that, given array array, the expression array decays to an expression with such a type which points to the first element) but, despite its function, std::shared_ptr is not a pointer type.
You would have to obtain the raw pointer first:
array.get()[n];
Where n is, of course, a valid array subscript.
This is also the case with std::unique_ptr (though note that, in that case, you do not need to supply your own deleter!).
in C++17, support for array of shared_ptr like in unique_ptr( c++11).
int main()
{
std::shared_ptr<double[]> array1 (new double [3]{4,5,6}, [](double * d){
delete [] d;});
std::unique_ptr<double[]> array2 (new double [3]{4,5,6}, [](double * d){
delete [] d });
}
Related
I am puzzled how both new int and new int[n] return an int*. Why doesn't the latter return an int**?
Here is some context: refer to the variable data below in a snippet from Goodrich, Tamassia, and Mount's 2nd ed. Data Structures and Algs in C++ textbook:
class Vect {
public:
Vect(int n);
~Vect();
// ... other public members omitted
private:
int* data;
int size;
};
Vect::Vect(int n) {
size = n;
data = new int[n];
}
Vect::~Vect() {
delete [] data;
}
(answers contains simplifications for the sake of explanations)
In C, a pointer to an array of ints would be of type int**, if I am not mistaken.
You are mistaken. In C it also would be int*, more precisely: int (*)[]
When you declare: int foo[] = { 1, 2, 3 }, the name of array (foo) can be in many cases effectively treated as pointer to its first element (1). The pointer to int is int*.
Additionally, why are we calling delete[] instead of delete, to delete an int* (data)?
delete deletes single object. delete [] removes dynamic array.
new int[n] cannot return a result of type int** because an int** value has to point to an object of type int* (a pointer object).
new int[n] allocates memory for an array of n objects, each of which is of type int. It does not create a pointer object.
The int* value it returns points to the initial (0th) element of the allocated array. Other elements of the array can be accessed by pointer arithmetic.
It could have been defined to yield a result of type int (*)[n], which is a pointer to an array of n int elements, except that (a) C++ doesn't permit arrays with non-constant bounds, and (b) even for something like int (*)[4], it's less convenient that int*.
C++, like its ancestor language C, treats arrays as second-class citizens. Array expressions, in most but not all contexts, are "converted" (adjusted at compile time) to pointer expressions, pointing to the initial element of the array object, and array elements are accessed using pointer arithmetic. (The indexing operator a[i] is syntactic sugar for *(a+i)).
Yes, it can all be confusing and counterintuitive.
Recommended reading: Section 6 of the comp.lang.c FAQ (most of it applies to both C and C++).
I have a corner case with regards to pointers and arrays.
How do I allocate a fixed-size (automatic) array on the heap? Let's get to the code right away to understand what I'm trying to ask:
typedef int ArrayOf10Ints[10];
int f()
{
ArrayOf10Ints * p = new ArrayOf10Ints; // Error: cannot initialize a variable of type 'ArrayOf10Ints *'
// (aka 'int (*)[10]') with an rvalue of type 'int *'
ArrayOf10Ints * q = new ArrayOf10Ints[1] // OK: allocates space for 10 ints
delete q; // Warning: use delete []
}
Why doesn't the expression to allocate p work? Why is the rvalue an int* and not a ArrayOf10Ints*? And why does q work?
Note: my goal is to understand the unexpected behavior allocating p and q. As others have pointed out there are many straightforward ways to solve this problem. For example, in my case, I'm using a pointer to denote that the array is optional—it may or may not exist—so I would do this instead:
boost::optional<std::array<int, 10> > optional_array;
This is a behavior of new that is somewhat surprising. Even though ArrayOf10Ints is an alias for int[10], when you use it in a new expression, the result is as if you were writing new int[10] instead.
This is specified in [expr.new]/5
When the allocated object is an array (that is, the noptr-new-declarator syntax is used or the new-type-id or type-id denotes an array type), the new-expression yields a pointer to the initial element (if any) of the array.
So in your example, the new expression returns an int *, hence the error.
A workaround is to do what you've shown
ArrayOf10Ints* q = new ArrayOf10Ints[1];
delete[] q;
or place the array in a struct, or use std::array.
Note that even if you were to write
int* p = new ArrayOf10Ints;
you must then use delete[] p because operator new[] is called in this case too.
Live demo
Note: for reasons beyond this discussion, I need to do exactly this
Certainly not, because wrong things don't work.
Use a std::array<int,10> instead. This code should work just smoothly:
typedef array<int,10> ArrayOf10Ints;
int f() {
ArrayOf10Ints * p = new ArrayOf10Ints;
// ...
delete p;
}
However I won't recommend you manage new and delete yourself, unless you're absolutely sure you need to.
I have created two routines, one to sort a vector in-place:
void Sort(double** vector, unsigned int vectorLength) {
//...
}
and one that returns a new array with the sorted result:
double* Sort(double* vector, unsigned int vectorLength) {
//...
}
Later using the sort method:
double* test = new double[5];
//...
Sort(*test, 5);
I receive the compiler error that 'none of the 2 overloads could convert all the argument types.'
Is not the type double*, a pointer to a double, not fundamentally different to a double**, a pointer to a pointer to a double?
How is this not clear to the compiler?
You get the error because *test expression is neither double* nor double** - it's a double, with no asterisks.
Passing test without dereferencing it would have worked:
double* sorted = Sort(test, 5); // Invokes the second overload
Note that you can sort in place even if you pass a pointer. Using an overload for this that requires an artificial &, works but it makes your API counter-intuitive.
A better approach would be defining a method with the same set of parameters, but a different name, for example
double* Sort(double* vector, size_t vectorLength) {
//...
}
void SortInPlace(double* vector, size_t vectorLength) {
//...
}
Your variable test has type double* and so *double has type double. Which means that you are attempting to pass double which matches neither double* nor double**.
One possibility is that you actually meant to create a sorted copy of the original array:
double* sorted = Sort(test, 5);
But I rather presume, since your call to Sort ignores the return value, that you meant to pass &test to sort in-place.
Sort(&test, 5);
However, that in itself indicates that your interface is badly designed. You would pass &test if you wanted the function to modify test. But you don't. You want the function to modify the array that test refers to. Your in-place sort function can, and should, be implemented by passing a parameter of type double*.
It is my opinion that you are abusing function overloading here. I would recommend that you choose a different design. I would use functions with different names.
Some other comments:
Use size_t for array lengths.
Use const to indicate that an input parameter shall not be modified.
It's very clear, but the expression *test when test has the type double * is the same as test[0], i.e. it's a double and not a pointer at all.
Perhaps you meant &test to take the address of the pointer.
*test goes directly to the value stored in that memory address. So, it goes to the double stored in the address test, which is a pointer to double.
So, you're passing a double value, not a pointer.
Your work would work with either of these two:
Sort(test, 5); // Passes a pointer to double.
or
Sort(&test, 5); // Gives you the address of test. Passes a pointer to a pointer to double.
class A {
public:
std::vector<int> & getIds(const int & item) const {
return ids[item];
}
private:
std::vector<int> * ids;
}
If ids is a pointer on a vector of ints, then why the method getIds, assuming it uses hidden vector's get operator [] by index, why it returns a reference to a vector of ints and not an int as I expect. Just do not understand this.
Could you please help me to convert it to Java? Please do not give minuses, try to help.
ids is presumably assumed to be a pointer to an element of an array of vectors, for instance:
A::A() : ids(new std::vector<int>[100]) { }
This is very poor style.
The declaration std::vector<int> * ids; says that this is a pointer to either a single object of type std::vector<int> or to (the first element of) an array of that type. The fact that operator[] is used on it in the member function shows that the second is the case.
Applying operator[] to a pointer (as in ids[item]) accesses an element (in this case, the element with number item) of the array the pointer points to. The fact that the type of the objects in the array (std::vector<int>) also has an operator[] defined doesn't matter because this code does not call that (you could call operator[] on such an object by adding another indexing operator, like ids[item][2], or by dereferencing the pointer, like (*ids)[2] (which is equivalent to ids[0][2]).
In C and C++, arrays are just pointers (except std::array, which I'm not talking about). The [] notation just hides some pointer arithmetic.
int foo[10]; //foo is essentially and int *
int bar;
bar = *(foo + 3); //This statement is equivalent to the next
bar = foo[3]; //This just means get what's pointed to 3 pointers away from the address foo
A std::vector is a class.
std::vector<int> *ids
just describes a pointer to an instance of std::vector<int>, not to the data that might be contained in it.
First of all, I want to reassure you all that I am asking this question out of curiosity. I mean, don't tell me that if I need this then my design has problems because I don't need this in real code. Hope I convinced you :) Now to the question:
For most types T we can write
T* p = new T;
now what if T is an array type?
int (*p)[3] = new ???; //pointer to array of 3 = new ???
I tried this:
typedef int arr[3];
arr* p = new arr;
but this doesn't work.
Is there any valid syntax for this or it is impossible in C++. If it is impossible, then why? Thanks
Edit: i am guessing I wasn't clear enough. I want to be able to use it in this situation:
void f(int(&)[3]);
int (*p)[3] = new ???;
f(*p);
To get a pointer to an array from new, you have to dynamically allocate a two-dimensional array:
int (*p)[3] = new int[1][3];
The reason you can't do it is that new int[3] already allocates exactly what you want, an object of type int[3]. It's just that what the new-expression returns, is a pointer to its first element. 5.3.4/1:
If the entity is a non-array object,
the new-expression returns a pointer
to the object created. If it is an
array, the new-expression returns a
pointer to the initial element of the
array.
Returning a pointer to the first element is what allows the 3 to be unknown until runtime, so I suppose that by knowing it in advance, you've tripped over flexibility that you aren't using.
I guess the ways around this are to reinterpret_cast back to the pointer type you want (not necessarily portable), or to allocate a struct containing an int[3] (and use a pointer to its data member).
[Edit: er, yeah, or FredOverflow's idea, which has neither disadvantage, but requires use of delete[] instead of delete.]
I guess the moral is, if you write templates that naively allocate some unknown type T with new, then the template won't work when someone passes an array type as T. You'll be assigning it to the wrong pointer type, and if you fix that (perhaps with auto), you'll be deleting it wrongly.
Edit in answer to j_kubik's question:
Here's one way to distinguish between array and non-array types. If you write a function like this, that returns an object that holds the pointer and is capable of correctly deleting it, then you have a generic new/delete for any type T.
#include <iostream>
template <typename T>
void make_thing_helper(T *) {
std::cout << "plain version\n";
}
template <typename T, int N>
void make_thing_helper(T (*)[N]) {
std::cout << "array version\n";
}
template <typename T>
void make_thing() {
make_thing_helper((T*)0);
}
int main() {
typedef int T1;
typedef int T2[3];
make_thing<T1>();
make_thing<T2>();
}
You could always use boost::array, which will be in C++0x.
Otherwise, any solution will be awkward at best: arrays are
broken in C, and C++ maintains compatilibity with C in this
respect. Fred Overflow offered one solution; even easier (but
syntactically noisy) would be to wrap the array in a struct:
struct A { int arr[3]; };
and allocate and manipulate this.
You just do
int *p = new unsigned int [3]
You can then use *p as a pointer or an array i.e. *(p+1) or p[1]