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.
Related
class Edge:
class Edge {
int dist = 0;
std::pair<Node, Node> ends;
public:
Edge() = default;
explicit Edge(const int idist) : dist(idist) { }
explicit Edge(const int idist, Node& end1, Node& end2) : dist(idist) {
ends.first = end1;
ends.second = end2;
}
~Edge() = default;
};
In the ctor explicit Edge(const int idist, Node& end1, Node& end2), why am I not allowed to use the syntax?:
explicit Edge(const int idist, Node& end1, Node& end2) : dist(idist), ends.first(end1), ends.second(end2) { }
This is just not allowed. As the syntax of member initializer list,
class-or-identifier ( expression-list(optional) ) (1)
class-or-identifier brace-init-list (2) (since C++11)
While ends.first and ends.second don't refer to class or identifier, they're expressions. You have to initialize ends in whole, e.g.
explicit Edge(const int idist, Node& end1, Node& end2) : dist(idist), ends(end1, end2) { }
What you're trying to do is allowed, but the way you're doing it isnt.
The initializer list specifies how to turn a random bit of memory containing uninitialized bytes into a valid object. The constructor can then do things with this new object, but the object state needs to be initialized beforehand. There are some caveats to this surrounding uninitialized values resulting from default initialization, but the only valid thing to do in an initializer list is call a member's constructor. (See here for more on default initialization.)
In your case, first and second are fields of a pair. You can't access them until the pair has been constructed. And even then, you couldn't necessarily re-initialize them the way you're attempting to.
The solution is to initialize the whole pair at once using one of its constructors:
explicit Edge(const int idist, Node& end1, Node& end2) : dist(idist), ends(end1, end2) { }
One of the fundamental features of C++ classes is that every object (absent malice) will be properly created by its constructor. Looked at the other way around, if a type has a constructor, then you must create objects of that type with the constructor.
In the constructor initializer list for Edge, each member of Edge gets constructed. Since std::pair has its own constructor, constructing an Edge object means constructing that ends member with the appropriate constructor for std::pair.
That's not something you should be trying to bypass. It's fundamental to sound programming. Constructors create objects. Doing your own thing risks creating things that haven't been properly created. Yes, in this particular case, you can probably get away with it. But there's nothing to gain from bypassing the constructor, and in other cases, that would cause significant problems. Don't bypass constructors. Let them do their job, so your job will be much easier.
Since C++14 (it might be C++11, i'm not sure), zero initialization happens on class construction on certain conditions, depending on the way the constructor is called.
Is there a way to ensure a raw value field (let's say a pointer) is never zero initialized ?
I guess not because it seems that zero initialization happens at the class level and not at the field level (surely a kind of memset(this, 0, sizeof(TheClass))), but I'm still hoping there is way, a hack, something...
The idea is to be able to initialize a field before a placement new is called so that that member is available during construction time.
According to cppreference's take on zero initialization:
If T is an non-union class type, all base classes and non-static data
members are zero-initialized, and all padding is initialized to zero
bits. The constructors, if any, are ignored.
If your object is a victim of zero initialization you're out of luck. And every object with static or thread local storage duration will always be zero initialized (except for constant initialization).
Is there a way to ensure a raw value field (let's say a pointer) is never zero initialized ?
It is possible, yes.
If the class is trivially default constructible, then simply default initialise the object, and its members will be default initialised as well:
int main() {
T t; // default initialised
If the class is not trivially default constructible, you can write a user defined constructor that leaves the member uninitialised (note that doing this is generally discouraged):
struct S {
int member;
S(){} // member is left default initialised
Warning: With a user defined constructor, the member would be left default initialised even if the object is value initialised.
Objects with static storage duration are always zero or constant initialised. Neither of those leave members uninitialised. Only way to avoid that initialisation is to not create objects with static storage duration.
The idea is to be able to initialize a field before a placement new is called so that that member is available during construction time.
If you mean your idea is to initialise a field of an object that hasn't yet been created with placement new, then the idea is not possible to implement.
Members are available during construction time. You don't need any tricks to achieve this. All members that can be used by their siblings in order of their initialisation. And the constructor body is run after all members have been initialised.
As #eerorika's answer suggests:
struct S {
int member;
S(){} // member is left default initialised
...note that doing this is generally discouraged
I would say that the clearest way to achieve this without confusing the user of the class would be not to use a int, but a std::aligned_storage_t as it is clear that this member is used just for storage of objects that might be created later rather than a value that can be used on its own.
Depending how much code you would like to write, this might be tedious, because you have to write placement news and use std::launder (since c++17) to be compliant with the standard. However, this is IMO the best way to express your intent.
cppreference gives a good example of how to use std::aligned_storage (with some modification, see comments):
#include <iostream>
#include <type_traits>
#include <string>
template<class T, std::size_t N>
class static_vector
{
// properly aligned uninitialized storage for N T's
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
std::size_t m_size = 0;
public:
// My modification here suppresses zero-initialization
// if initialized with empty braces
static_vector() {};
// Create an object in aligned storage
template<typename ...Args> void emplace_back(Args&&... args)
{
if( m_size >= N ) // possible error handling
throw std::bad_alloc{};
// construct value in memory of aligned storage
// using inplace operator new
new(&data[m_size]) T(std::forward<Args>(args)...);
++m_size;
}
// Access an object in aligned storage
const T& operator[](std::size_t pos) const
{
// note: needs std::launder as of C++17
return *reinterpret_cast<const T*>(&data[pos]);
}
// Delete objects from aligned storage
~static_vector()
{
for(std::size_t pos = 0; pos < m_size; ++pos) {
// note: needs std::launder as of C++17
reinterpret_cast<T*>(&data[pos])->~T();
}
}
};
int main()
{
static_vector<std::string, 10> v1;
v1.emplace_back(5, '*');
v1.emplace_back(10, '*');
std::cout << v1[0] << '\n' << v1[1] << '\n';
static_vector<std::size_t, 10> v2{};
// This is undefined behavior.
// Here it's just used to demonstrate that
// the memory is not initialized.
std::cout << v2[0] << "\n";
}
Run it on compiler explorer
However, since the c++ standard only mandates when zero-initialization must happen, but not when it must not happen, sometimes you really have to wrestle with your compiler in order to suppress zero initialization.
See this question and this question for more details.
Please take a look at this code:
template <typename T>
class matrix
{
private:
std::vector<T> vec_;
public:
matrix(size_t rows , size_t cols) : rows_(rows) , cols_(cols) , vec_(rows_ * cols_)
{
}
size_t rows_;
size_t cols_;
};
This is a way to declare a class. I am just wondering where the std::vector is being allocated and where it is being initialized?
What happens when I declare a variable? Is the space for it allocated in the stack before the constructor is called or is allocated and initialized in the constructor?
What is the difference between declaring a vector with size 10
std::vector<T> vec_(10);
and calling the constructor of vec_ with the size 10 in the constructor?
matrix() : vec_(10)
I wanted to understand how objects are allocated and initialized in C++.
Also I can create a constructor without calling the constructor of std::vector
matrix() {}
What is happening here? Since I am not calling the constructor for the vector does the compiler call it own its own? Can the vector object utilized? Or is it called automatically because I declared std::vector to be a variable in my class?
Also does initializing as std::vector vec(10), have the same effect as calling resize/reserve. Which is it closer to?
The members of a class object are initialized in the order they are declared. In this case it is this order:
private:
std::vector<T> vec_;
public:
size_t rows_;
size_t cols_;
so when executing the initializer list
matrix(size_t rows , size_t cols) : rows_(rows) , cols_(cols) , vec_(rows_ * cols_)
the real order is
vec_(rows_ * cols_), rows_(rows) , cols_(cols)
which is using rows_ and cols_ before they are initialized. And bad things will happen.
that code:
private:
std::vector<T> vec_(10);
doesn't compile as Bo noted. But you could do this in a legal & performant way in c++11 (as performant because of copy elision: assignment operator is very likely to be turned into a copy constructor because object is being initialized at this very line):
private:
std::vector<T> vec = std::vector<T>(10);
So, that previous construct and this one:
public:
matrix() : vec_(10)
do the same: vec_ will be built once, with 10 elements inside it, exactly like resize would do, except that it's faster because you don't have to create an empty vector and resize it.
For the second part of your question: If you create your instance using a default constructor, the compiler will call the default constructor for all class members, so it is perfectly safe.
Beware: POD objects are not zeroed, which may be a problem, so it's better to set pointers to nullptr, integers to 0, etc... or you get uninitialized values, not good when entering the destructor with an invalid pointer passed to delete ...
Of course, it will fail to compile if one of the members has only non-default constructors (needing a parameter).
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?
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....