This is probably a simple question but I have this template class:
template<typename Type>
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
The code is from a course pdf file where it says buff(new Type[n]) is unsafe. I don't understand why it's unsafe, isn't size_t generally unsigned? Can I have an example where it could have a compile and/or run-time error?
The code is "unsafe" in the fact that it relies on n being constructed before buff. This dependency adds brittleness to the code.
When you construct the members of the class they are constructed in the order the are declared in the class, not how they are called in the member initialization list, so if the code was changed to
template<typename Type>
class Array {
Type* buff;
size_t n;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
Then when you do buff(new Type[n]), n is uninitialized and you have undefined behavior.
First of all the order, the initializations for the constructor are executed is not determined by the order they are written down, but by the order the initialized fields appear in the code:
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
Here first n will be initialized and then buff.
class Array {
Type* buff;
size_t n;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
Now first buff will be initialized and then n, so n has no defined value in that case.
Using initialization lists for constructors is good practice, but be careful, that you don't create any assumptions on the order.
Generally it is a good idea to refrain from owning raw pointers. If you use smart pointers instead, you can not forget to release the data.
In the specific case, you might also want to use std::vector instead of a C-style array. That handles all the allocation, reallocation, releases, etc. in a thread safe manner for you. It seems like you are trying to write something like your own std::vector. Please only do that for educational purposes. Always prefer the standard implementation in production code. You probably won't get it better for quite a while. If you did, you would ask different questions here ;-)
First of all, you have a memory leak. But the question probably isn't about that. So let's assume you have a destructor that deallocates the array.
template<typename Type>
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
~Array() { delete[] buff; }
};
Now this particular code is perfectly safe. No exception can be thrown while assigning n_, the order of initialization is correct and buff is the only raw pointer in your class. However, as you start expanding your class and writing more classes, the risk of a memory leak increases.
Imagine that you need to add one more members to the class Array:
template<typename Type>
class Array {
size_t n;
Type* buff;
SomethingElse xyz;
public:
Array(size_t n_): n(n_), buff(new Type[n_]), xyz(n_) {}
~Array() { delete[] buff; }
};
If the constructor of SomethingElse throws, the memory allocated for buff will leak, because the destructor ~Array() will never be called.
Modern C++ calls pointers such as Type* buff raw pointers because you are responsible for deallocating storage yourself (taking exceptions into account), and introduces tools such as std::unique_ptr and std::shared_ptr that can take care of storage deallocation automatically.
In modern C++ you could write your class like this:
template<typename Type>
class Array {
size_t n;
std::unique_ptr<Type[]> buff;
public:
Array(size_t n_): n(n_), buff(new Type[n_]) {}
};
Notice the absence of a destructor. The unique_ptr will take care of calling delete for you.
Note also no dependency on class members inside the initializer list (simply writing new Type[n_] instead of new Type[n] makes your code more robust)
C++98 Standard 12.6.2.5.4 (I don't expect new versions to have relaxed this).
— Then, nonstatic data members shall be initialized in the order they
were declared in the class definition (again regardless of the order
of the mem-initializers).
So the order of initialization is defined according to this.
If you want an example of how to crash it simply make sizeof(Type)*n > total memory in your system.
It is not safe to call new operator inside initialization list.
if new fails the destructor of Array will not be called.
here is a similar question.
Are there any issues with allocating memory within constructor initialization lists?
Related
I'm new to C++, I have a class hold some memory, the class looks like:
class MyClass
{
public:
MyClass (int s)
{
if (s <= 0) _size = 1;
else _size = s;
data = new int[_size]; // may throw exception
}
private:
int *data;
int _size;
};
To my knowledge, throw exception in constructor is unsafe, so I put the malloc to a init function.
class MyClass
{
public:
MyClass (int s)
{
if (s <= 0) _size = 1;
else _size = s;
}
void init()
{
data = new int[_size];
}
private:
int *data;
int _size;
};
my problem:
memory alloc in constructor or init function, which is better
if I chose init function, how to ensure init function has called before call other member function?
To my knowledge, throw exception in constructor is unsafe, so I put the malloc to a init function.
No, it is not "unsafe". It's the way to fail a constructor. You may be thinking about destructors aborting or other issues with exception safety guarantees.
"Init" functions introduce a two phase approach to constructors that increases complexity. Unless you need to work in an environment without exceptions, avoid them. Factory functions are another way to do things in that case.
memory alloc in constructor or init function, which is better
Constructor. See std::vector.
if I chose init function, how to ensure init function has called before call other member function?
Statically you cannot without a run time check or external tools.
You can set a flag on your init function and then check in every member function.
if I chose init function, how to ensure init function has called before call other member function?
You can't, and this is a big problem! What you have stumbled on here is a concept called Resource Acquisition is Initialisation (RAII). This is roughly what you have written in part 1. If you add a destructor:
~MyClass() {
delete[] data;
}
Then you have started working towards one of the most useful c++ containers, the std::vector. This does a similar thing to what you are trying to achieve here.
There is even a cpp core guideline about this: A constructor should create a fully initialised object.
To my knowledge, throw exception in constructor is unsafe
Actually this is untrue. In fact the very next core guideline is If a constructor cannot construct a valid object, throw an exception.
TLDR: Use option one, init is bad in this case.
The point in constructors are that you can be sure that the constructor is called before any other member function.
Init functions come from c and are not considered best practice
In addition the valid answers above, you might consider "encapsulating" the allocation and de-allocation of memory using an existing standard library class: std::unique_ptr<int>. Using it may save you the need to implement a bunch of methods (e.g. copy constructor, move constructor, assignment operator and destructor; see the rule of five).
Also, std::size_t is more idiomatic for storing amounts of allocated memory.
Finally, names starting with _ are generally reserved, and we avoid them.
So:
#include <memory>
// ...
class MyClass {
public:
using size_type = std::size_t;
using value_type = int;
MyClass (size_type size) :
size_(std::max<size_type>(size,1)),
data_(std::make_unique<value_type[]>(size_))
{ }
// etc. etc. - but no need for copy ctor, move ctor, assignments and dtor
private:
std::unique_ptr<value_type[]> data_;
size_type size_;
};
I am attempting to learn C++ and programming and thus I'm implementing some of the data structures in the STL.
As for my version of vector, I have two constructors, where one of them allows you to specify a location in memory for storage, if you so please. If not I have set the address of the internal data to nullptr.
I'm wondering how this will behave. Using the first constructor, I assume m_data will be set to nullptr, but what happens in the second case? Is m_data first set to nullptr, and then overwritten in construction? Is there a reason to keep the definition as nullptr in the definition, or should I just use the initializer list in both cases?
Here is the code:
template <typename T, size_t S = 0>
class custom::vector {
private:
T* m_data = nullptr;
size_t m_size;
size_t m_capacity;
T* m_future_data = nullptr;
size_t m_future_cap;
public:
// Constructors
vector() : m_size(S), m_capacity(S) {}
vector(T* destination, const size_t& capacity) : m_size(S) {
m_data = destination;
m_capacity = capacity;
}
// .... rest of class
It's tricky so see in your specific case, because T* is a pointer, and pointers do not have constructors. If you have a member with class type, you can see which constructor is called.
What you'll then see is that members are initialized (ctor, not assignment) once. The initializer used will be the one in the initializer list, if one is available there, or from the member definition if there's no initializer in the initializer list.
As for the question whether you should keep the default for m_data, I'd say no. The main reason for that is that you should have a proper class invariant, and I suspect that invariant should link m_data and m_size. But m_size has no default in its definition - it could be S, perhaps. But you should be consistent there.
I am designing a custom C++ template container like this:
template <class C>
class Container {
public:
...
void clear() {
// should I call destructor on elements here?
for (i...)
array[i].~C(); // ?
}
~Container() {
delete [] array_;
}
private:
C* array_;
};
Should I call the destructor on elements manually inside clear()? If I don't, they will be called later when the container is destroyed (since I delete [] array_ in destructor), but this is not the expected behaviour (we expect them to be destroyed right inside clear(), just like std::vector does).
However, if I do call the destructors, that memory remains in place and when I will be adding new elements on top of the old ones (which now are destroyed), the assignment operator will be called on those destroyed elements, and this may result in undefined behavior.
Suppose I have a class:
class Foo {
public:
Foo() { foo_ = new int; }
~Foo() { delete foo_; } // note I don't explicitly set foo_ to nullptr here
Foo(Foo &&f) {
f.foo_ = std::exchange(foo_, f.foo_); // standard practice in move constructors
}
};
OK, this may look good so far, but now if I make a
Container<Foo> c;
c.add(Foo());
c.clear();
c.add(Foo());
when calling clear() the destructor of the initial Foo is called, leaving its foo_ pointer dangling. Next, when adding the second Foo, the temporary R-value is exchanged with the old contents of the destructed object and when the temp will be destroyed, its destructor will try to delete the same dangling pointer again, which will crash.
So, how to correctly clear() a container without leaving room for double deletion?
I also read that its recommended not to set pointers to nullptr in destructor in order not to not hide potential dangling pointer problems.
I'm a little confused about how to approach this.
EDIT:-------------------
There seems to be no compromise-free approach for a template container.
So far I see these options, as others pointed out:
completely delete the underlying array in clear(); this would be
safe but not performant. I cannot use this option however since I'm
implementing a lock-free multi-threaded container.
instead of calling the destructor on elements in clear(), I would call the default constructor instead: array[i] = C(); This seems
like the best solution so far - except it still implies an extra
default construction.
use placement new in add() - this seems to work for copy-construction. However, move-construction into an uninitialized object still seems problematic because most move constructors are implemented with exchanges between pointers - this would leave the source object (moved from) invalid.
The delete [] array_ will invoke the destructor for each element of array_ (assuming C is a type with a destructor).
Invoking a destructor twice on any given object gives undefined behaviour.
This means your clear() member function should not directly invoke destructors for the array elements.
If you insist on having a separate clear() function, which does not interfere with working of the destructor, simply implement it as
void clear()
{
delete [] array_;
array = nullptr; // NULL or 0 before C++11
}
This won't interfere with the destructor since operator delete has no effect if acting on a NULL pointer.
As you specified in the comments, in the unassigned cells you have objects constructed by the default constructor (T()). That may be bad for performance, but certainly preserves the object abstraction.
Instead of deleting the entries, just replace them with the ones constructed by the default constructor:
template <class C>
class Container {
public:
...
void clear() {
for (i...)
array_[i] = C();
}
~Container() {
delete [] array_;
}
private:
C* array_;
};
Alternative implementation
On the other hand, I would propose a more performant approach, although it breaks the nice C++ abstraction. You could allocate the memory without invoking the unnecessary default constructor:
template <class C>
class Container {
public:
Container(int n) {
array_ = (C*)malloc(sizeof(C)*n);
size_ = 0;
}
...
void clear() {
for (... i < size_ ...)
array_[i].~C();
size_ = 0;
}
~Container() {
clear();
free(array_);
}
private:
C* array_;
int size_;
};
Here you do call destructors on all the initialized elements, but you do not call it second time, because you keep track of which are initialized, which are not.
Suppose I have a class...
class Foo
{
public:
Foo(int size);
private:
const int size;
int data[];
};
Supposing that the size field is set immediately at instantiation, how can I set the length of data based on that size input?
I would ordinarily use a std::vector here, but I am writing a library for Arduino, so that won't fly, and I'm trying to avoid external dependencies if I can.
You are out of luck here, as C++ must know the size of the array at compile time (in other words, the size must be a constant expression). Your options are to either use dynamic allocation
int* data;
then allocate with new int[size]; in the constructor initialization list, or, better, use std::unique_ptr<> (C++11 or later), which is a light wrapper around a raw pointer and deletes its allocated memory at scope exit, so you won't have to manually delete[]
class Foo
{
private:
std::unique_ptr<int[]> data; // no need to manually delete
public:
Foo(int size): data{new int[size]} {}
};
A third option is to make Foo a non-type template class (assuming you know the size at compile time, which is what it seems to be happening, at least judging from your question)
template<std::size_t size>
class Foo
{
int data[size];
public:
Foo()
{
// constructor here
}
};
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....