Im working on a template class which represents a managed Array.
E (*data)[];
data is my array.
data = new E[size];
And this, it doesn't like. it throws me;
cannot convert Component* to Component (*)[] in assignment
What gives?
Also can anyone explain why E is denoted with a * even though I didn't pass a pointer type into my template type?
E (*data)[];
data is a pointer to an array of E, not a pointer to E (and not to be confused with an array of pointer to E). There is a very appreciable difference.
EDIT: To help you understand...
new is returning to you a pointer to E, yet you have declared data as a pointer to array of E. An array is a type in C! It is not simply a pointer in disguise. An array may decay into a pointer in certain situations, but it doesn't go both ways.
EDIT 2:
Per your comment:
I was under the impression that I was creating a new array of E pointers?
Go to http://cdecl.org/
First, type in:
int (*data)[];
Read what it says. Now type:
int *data[];
Read again and note that it is not saying the same thing. One as a pointer to array of int, one is an array of pointers to int. Big difference.
If you want to dynamically allocate an array of pointers then data should be declared as:
E **data;
And then
data = new E*[size];
Just make your data member an E*. You will still be able to index array-style ala data[i] (that notation, when used with a pointer and integral value, basically means add the pointer and i times the size of the pointed-to objects).
EDIT:
template <typename E>
class X
{
X(int initial_size) : data_(new E[initial_size]), size_(initial_size) { }
X(const X& rhs) : data_(new E[rhs.size_]), size_(rhs.size_) { std::copy(...); }
~X() { delete[] data_; }
void capacity(int new_size)
{
E* p = new E[new_size];
std::copy(p, p + new_size, data_);
delete[] data_;
data_ = p;
size_ = size;
}
...
private:
E* data_;
int size_;
};
Related
I have an std::vector-like class (actually TArray from Unreal Engine) that has a function "AddDefaulted" which adds an element and returns the index as integer to the newly added element. It also has an operator as member-function that returns.. well.. the element at that index. Simplified something like that:
template<class T> struct TArray
{
// omitted initialization and typical std::vector-like implementation stuff..
T* data;
T* GetData() { return data; }
int AddDefaulted() { /* might change "data" if array grows */ }
T& operator[](int i) { return GetData()[i]; }
}
Now I stumbled upon a call to the operator[] and AddDefault in one expression:
TArray<X> a = ...;
X& x = a[a.AddDefaulted()];
Well, it looks unsuspicious, but I wonder.. could that be illegal? My thinking is: Could the compiler inline operator[] and then first evaluate the data pointer and then evaluate AddDefaulted and so accessing the "old" data array?
I have an assignment where I have to use a linked list of node with void* as the data. I would be filling the nodes with an object. I want to know some way of accessing the members of the object after it is in the linked list, other than casting it to the class. Is it possible? Also, here's a chunk of my code just in case it helps clarify my question.
struct Node
{
void* data_;
Node* next_;
Node()
{
data_ = 0;
next_ = 0;
}
};
class Star
{
private:
char name_[ENTRY_SZ];
long temperature_;
double luminosity_;
double mass_;
double radius_;
public:
static char filePath_[ENTRY_SZ];
Star(char* name);
void SetTemperature(char* temp);
void SetLuminosity(char* lum);
void SetMass(char* mass);
void SetRadius(char* rad);
void PrintToConsole();
void AppendToFile();
};
I want to be able to call the PrintToConsole function after it is in a void*.
You cannot work with the pointee of a void* without first casting it first. If it points at a Star, for example, you could do something like this:
static_cast<Star*>(data_)->PrintToConsole(); // Haha, Star star!
That said, in C++, it's pretty unusual to store things like this. You're much better off using a template class so that you get back the type information you need.
No. You have to cast it to the appropriate object.
I would question the reason on using void pointers.
I would also suggest a dynamic cast might be better
You should cast it to the class. But if you really don't want to, you can use the offsetof macro:
The macro offsetof expands to a constant of type std::size_t, the
value of which is the offset, in bytes, from the beginning of an
object of specified type to its specified member, including padding if
any.
If type is not a standard layout type, the behavior is undefined.
If member is a static member or a member function, the behavior is
undefined.
But you should just cast it to the class.
EDIT: Ah, I see you want to access a method of the class. That's not possible. You should cast it to the class.
Since this is an assignment, you might be best to ask your teacher/mentor as to what their intents are for using a void* type in C++; void* types are not inherently bad, but there other ways of achieving similar results while maintaining language consistency.
To answer directly:
I want to know some way of accessing the members of the object after it is in the linked list, other than casting it to the class. Is it possible?
Yes it is possible, but not using the void* idiom. Using your code as an example, you would indeed have to cast to the appropriate type and be certain the types pointed to are compatible if you keep the void*, example:
struct Node
{
void* data_;
Node* next_;
Node()
{
data_ = 0;
next_ = 0;
}
};
class Star
{
public:
void PrintToConsole() {} // empty to avoid not-defined errors
};
int main() {
Node n;
n.data_ = new int(42);
static_cast<Star*>(n.data_)->PrintToConsole(); // this will compile fine, but what happens here is undefined
delete static_cast<int*>(n.data_); // can't delete void*, have to cast
return 0;
}
Again, since this is an assignment, your professor is probably just trying to teach about pointers and casting or the type system and you probably haven't learned about C++ templates, but since you asked, here's your code using templates:
template < typename T >
struct Node
{
T data_;
Node* next_;
// use member init list to construct default T object
Node() : data_(), next_(0)
{
}
};
class Star
{
public:
void PrintToConsole() {} // empty to avoid not-defined errors
};
int main() {
Node<Star*> n;
n.data_ = new Star();
n.data_->PrintToConsole(); // OK
delete n.data_; // no cast needed since data_ is a Star* type
Node<int*> n2;
n2.data_ = new Star(); // compiler error (data_ is of type int* not Star*)
n2.data_->PrintToConsole(); // compiler error (int has no member named PrintToConsole)
delete n.data_;
return 0;
}
This is just a simple example to illustrate what you were asking and it's still probably best to ask your teacher for more clarification if your confused on the topic.
Hope that can help.
I tried to write an "inline-vector" class for storing a number of elements on the stack conveniently:
template<typename T, size_t size_ = 256>
struct InlineVector{
T content[size_];
size_t num;
T() : num(0) {}
~T() { for(size_t s = 0; s < num; s++){ content[s]->~T(); } }
template<typename _Up, typename... _Args>
void emplace_back(_Args&&... __args) { new (&content[num++]) T(__args); }
T& get(size_t i) { assert(i < num); return content[i]; }
}
For efficiency reasons, I want that content is not initialized in X's constructor, even if T has only a non-trivial constructor. As you see, the content is intialized later with placement new when it is actually inserted. However, C++ seems to enforce that all elements of content must be initialized in the initialization of X. For example, this does not compile:
struct Y{ Y(int){} }
X<Y> foo; // compile error, no constructor for Y is called in the construction of X
So, how is it possible to have an array member that is not initialized, even if the array element type needs a constructor?
By making the array an array of chars and later placement new-ing into it. char is the only type you're allowed to do this with.
Whilst you're at it, then, you might as well write your own allocator instead (with the array as one of its members), and plug it into std::vector rather than re-inventing the whole shebang.
std::vector<int, YourAllocatorType<int, 256>> v;
So, how is it possible to have an array member that is not initialized, even if the array element type needs a constructor?
It's not for T x[n]... but you can point any old T* at properly aligned uninitialised memory, then manage your own ad-hoc construction and destruction. You could also explore creating a union with your type and say a char, and have an array of the unions....
I am not very familiar with the use of template and overloading, and I was asked to correct a code as a practice exercise. I corrected all that I can manage to identify, but I am not sure what did I overlook. I apologize if the variable name sounds bad to you, they all came with the original broken code.
Part of the error message is
CSL.cpp: In member function 'void CSL::showList() [with T = int]':
CSL.cpp:106: instantiated from here CSL.cpp:26: error: subscripted
value is neither array nor pointer
The code itself:
template<class T>
CSL<T>::CSL(T *d, int s) : data(*d), size(s)
{
}
template<class T>
void CSL<T>::showList() //Function with problem.
{
cout<<"Comma separated list:"<<endl;
for(int x = 0; x < size; ++x)
{
cout << data[x];
if(x != size + 1)
cout << ": ";
}
cout << endl << endl;
}
int main()
{
someCustomers[0].setCustomer("Zaps", 23.55);
//...
someCustomers[5].setCustomer("Curtin",56999.19);
CSL_Size = sizeof(someInts)/sizeof(someInts[0]);
CSL<int> CSL_Integers(someInts, CSL_Size);
//...
CSL_Size = sizeof(someCustomers)/sizeof(someCustomers[0]);
CSL<Customer> CSL_Customers(someCustomers, CSL_Size);
CSL_Integers.showList(); //Problem starts here
CSL_Doubles.showList();
CSL_Videos.showList();
CSL_Customers.showList();
return 0;
}
Your CLS class template accepts a type parameter T and defines a data member data of that type.
When you are providing int as a type argument for T, the type of the internal data member data is, therefore int.
In your showList() function, you are then trying to apply the subscript operator to that int, passing another integral value as the input to operator []:
cout << data[x];
But that's illegal, since there is no pointer arithmetic involved (data is not array, nor a pointer, and neither is x). That basically amounts to doing something along these lines:
42[1729]
Which is obviously non-sense. You probably meant to have T* as the type of data, and rewrite your constructor as:
template<class T>
CSL<T>::CSL(T *d, int s) : data(d), size(s)
// ^^^^^^^
// Just pass d instead of *d
{
}
Notice, however, that leaving the ownership of the encapsulated container to clients is an awkward design decision. This way, you will have to make sure the CSL object won't outlive the array (otherwise, data will be a dangling pointer).
For this reason, your CLS class should contain a copy of the array provided as the input to the constructor. And in order to avoid making mistakes with dynamic allocation, deletion of the array, the Rule of Three, the Rule of Five and whatnot, you should be using RAII wrappers such as std::vector for this purpose.
I have been asked in an assignment to implement IntList, which is dynamic int array list having "int *p" as its private member. After implementing all the methods (add, find, delete, etc) , I couldn't find out how to do implement a public method returning:
Get a read-only pointer to the underlying Array.
How can I return a pointer having *p numbers, where it's content cannot be change?
You need to write the code similar to this one:
class A {
int *tab;
public:
A() {
tab = new int[3];
tab[0] = 1;
tab[1] = 4;
tab[2] = 6;
}
const int * get() {
return tab;
}
};
int main() {
A *a = new A();
//(a->get())[0] = 2;
}
Then you'd be able to see the content of the table, but changing it's value outside the class won't be possible.
Trying to uncomment the last line will result in getting this
error: assignment of read-only location ‘* a->A::get()’ :)
You'll have to use the const keyword in the method signature to tell the compiler that what is being returned is constant, or nonmodifiable. Since you're returning a pointer, you're also going to have to figure out how to differentiate between "pointer that can not be modified", and "pointer to data that can not be modified", and how to specify which one you want.
You declare a pointer to a constant type like so
type const *
So a declaration of a member function returning a pointer to a const type looks like this
type const * memberfunction();
Replace the word type with whatever type your are returning.
What you return cannot be changed as it is const. So it cannot appear on the left hand side of an assignment operator i.e = ; and it cannot be passed into a function or method via a non const parameter unless you cast away the constsness which I wouldn't recommend in general.