Say I have a struct (or class) with a dynamic array, its length, and a constructor:
struct array {
int l;
int* t;
array(int length);
};
array::array(int length) {
l=length;
t=new int[l];
}
I assume everything so far is legal: this is how I would write it (although it maybe not be the only way to do things), but I have seen the following code, that somewhat seems to work:
struct array {
int l;
int* t = new int[l];
array(int length);
}
array::array(int length) {
l=length;
}
It looks bad, so I wonder if this works out of sheer luck and undefined behaviors, or if there is some internal rule that makes this code work fine.
This code is not correct.
int* t = new int[l]; will happen before l=length;, thus reading the uninitialized variable l. Member initializers are handled before the constructor's body runs.
array::array(int length) : l{length} {}
instead would work because l is declared before t.
However, doing this "by hand" is a bad idea to begin with. You should be using std::vector.
The 2nd code snippet might have undefined behavior.
The data members are initialized at the order of how they're declared. For class array, when t is initialized l is not initialized yet. For objects with automatic and dynamic storage duration l will be initialized to indeterminate value, then the usage of l (i.e. new int[l]) leads to UB.
Note that l=length; inside the body of the constructor is just assignment; the initialization of data members has been finished before that.
BTW: With member initializer list the 1st code snippet chould be rewritten as
array::array(int length) : l(length), t(new int[l]) {
}
Related
The following C++ code compiles with no errors or warnings, but for some reason when I print out the contents of foo.arr I just get junk values. It looks like the array member is not being properly initialized.
template<int n> class C{
public:
const uint8_t (&arr)[n];
const int length = n;
C(const uint8_t (&arr_arg)[n]):arr(arr_arg)
{}
};
int main() {
C<5> foo{{1,2,3,4,5}};
for(int i =0;i<foo.length;i++){
printf("foo.arr[%d]=%2x\r\n",i,foo.arr[i]);
};
}
But if I write it like this, foo.arr is correctly initialized when I print out the results.
int main() {
const uint8_t x[]{1,2,3,4,5};
C<5> foo{x};
for(int i =0;i<foo.length;i++){
printf("foo.arr[%d]=%2x\r\n",i,foo.arr[i]);
};
}
Why does the second case work, but the first one doesn't?
One would think that the compiler would create a block of five bytes for the constant data {1,2,3,4,5} and then the initializer for foo would point foo.arr to that block of bytes. It seems that whatever foo.arr is pointing either not where that data is, or else that data doesn't exist any more by the time I print it out (possibly overwritten).
C<5> foo{{1,2,3,4,5}};
The constructor's parameter is a reference to a temporary object that gets destroyed after the constructor call finishes. All subsequent reference to this object results in undefined behavior.
C<5> foo{x};
Here, the constructor's parameter is a reference to an object that remains in scope and continues to exist as long as it is subsequently referenced and used.
It seems that ... data doesn't exist any more by the time I print it out (possibly overwritten).
That is, indeed, what's happening.
Title may be misleading, not sure how to explain this but I'll provide an example.
So I have this structures:
struct mom {
public:
static constexpr auto create(int i) {
return a(i);
}
struct a {
int* m_a;
constexpr a(int i) : m_a(&i) {
change();
}
constexpr void change(void) {
*m_a += 100;
}
struct b {
public:
static int get(a* import) {
int* arr = new int[10];
arr[1] = *import->m_a;
return arr[1];
}
};
};
};
And I call it like this:
printf("%d", mom::a::b::get(&(mom::create(10))));
It is supposed to add 100 the 10 (or any other value you give it) at compile time, then at runtime to store it into a dynamic array and return it to you.
This code is almost exact to my project, but it has a problem: instead of adding 100 to the value, it returns a random big number (-95321314). I tried debugging it and it is all fine until I call the get() function in which I have no idea what is happening that causes this
any ideas?
The problem is m_a is a pointer to a temporary, as soon the stack unwinds further usage of that temporary is undefined.
Specifically, the call to mom::create(10) returns a struct a, whose member m_a is no longer pointing to a valid region of memory. Subsequent usage of that member will be undefined (e.g. the copy into b's array in get())
A simple "fix" would be to change m_a(&i) -> m_a(new int(i)), although note that this introduces a memory leak and changes the memory layout entirely. Since it's not clear to me what your design goals are, it's a reasonable alternative to get things "working".
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'm not exactly sure how to pose this question so I'll start with some example code:
//header file
class A
{
public:
A();
private:
int x;
std::string arr[x];
}
//cpp file
class A
{
public:
A()
{
/*code to get the value of x from a cmd call*/
}
}
Is this code valid? More specifically, can I have my string array in my header file be of size x, even though x is not specifically given a value until an A object has been created?
If this doesn't work, is my only other option to use a dynamically allocated array?
The code is not valid. You should use a vector instead.
class A
{
public:
A();
private:
int x;
std::vector<std::string> arr;
};
A::A () : x(command_gets_x()), arr(x) {}
Since arr is being initialized by the value of x, the constructor only works when x precedes arr in A (as it is in your definition). However, if the only purpose of x is to track the size of the array, it is not necessary, since a vector has the size() method.
class A
{
public:
A() : arr(command_gets_x()) {}
int x () const { return arr.size(); }
//...
private:
std::vector<std::string> arr;
};
It's not valid. Array sizes must be constant expressions. Yes, you'll have to use dynamic allocation, though not necessarily directly. You can just use std::vector.
No, that's not possible, for one C++ doesn't have variable length arrays, and further, the array size must be a compile time constant.
You can in the constructor allocate an array with new, or, better use a std::vector.
No, you can't initialize arrays with non-const expressions. This will work, and is close to your original intent:
class A
{
...
const int x = 3;
std::string arr[x];
};
And in the .cpp file:
int A::x;
I found that on my mac, in x-code I could do the following
int x = foo() // get some value for x at runtime
int array[ x ];
but that is seriously uncool!! I just read yesterday that some compilers allow dynamic allocation on the stack, but I would recommend that you stay well clear of that.
If the value of x is not known until runtime, then you cannot allocate an array of size x until runtime. Think about what the compiler does: can an array of size x be allocated if we don't know how big x is? The only remaining option is to allocate at run-time (aka dynamically allocate).
I am currently taking a c++ course and trying to get a deep understanding of the whole thing.
I came up with some theories, it would be great if somebody could confirm them:
Every variable (local,global,staic,member and non-member) is guaranteed to have its ctor called before first use
The ctors of primitives like int are essentially no-ops, so we have explicitly assign a value, there is no default zero value.
the following classes are semantically the same (and should generate identical code)
class A
{
int n;
};
and
class A
{
int n;
public:
A() : n() {}
};
and
class A
{
int n;
public:
A() { n = int(); }
};
The variable n is in every case still uninitialized.
EDIT:
It seem that I absolutetly underestimated the complexity of this subject, most of my assumptions were wrong. Now Iam still trying to find out the basic rules of object initialisation.
I'm afraid you are wrong. When you say:
int n = int();
Then n (and all other POD types) will zero initialised.
Also, make sure that you are very clear in your mind about the difference between initialisation and assignment - this is quite important in C++:
int n = int(); // initialisation
n = 0; // assignment
You might find this interesting.
The difference between new Foo and new
Foo() is that former will be
uninitialized and the latter will be
default initialized (to zero) when Foo
is a POD type. So, when not using the
form with the parens, the member "a"
can contain garbage, but with the
parens "a" will always be initialized
to 0.
No, the variable is only left uninitialised in the first case.
For a member that is a class with a user-defined constructor, the situation is simple: a constructor is always called.
Built-in types (and 'plain old data' structs) may be left uninitialised, as in your first example. Although they don't have user-supplied constructors, using the construction syntax (your other two examples) initialises them to zero.
The reason for this slightly tricky rule is to avoid undue overhead; for example if you defined:
struct S
{
int array[1024*1024];
};
with the intention of only assigning values as you needed them, you wouldn't want the compiler to whitewash 4Mb of memory with zeroes whenever you construct one.
class A
{
int n;
};
Only memory is allocated, no initialization done for n.
class A
{
int n;
public:
A() : n() {}
};
Here n is initialized with 0.
class A
{
int n;
public:
A() { n = int(); }
};
Here n is first constructed (without any default value),
then int() causes a temp int to be created with value 0
Which is then assigned to n;