Does the following program have undefined behavior?
#include <iostream>
#include <vector>
struct Foo
{
const std::vector<int> x;
};
int main()
{
std::vector<int> v = {1,2,3};
auto f = new Foo{v};
const_cast<int&>(f->x[1]) = 42; // Legal?
std::cout << f->x[1] << "\n";
}
Note that it not using const_cast to strip constness from f->x, but instead stripping constness from f->x[x], which presumably is represented by a separate array. Or is a translation allowed to presume that f->x[1] is immutable after it is created?
There is no Undefined Behavior in your example.
The above code does not invoke undefined behavior because the underlying data (int) is mutable. Let's look at a simpler example.
#include <iostream>
struct IntPtr {
int* data;
};
int main() {
int a = 0;
const IntPtr p { &a };
*p.data = 10;
std::cout << a; // Prints 10
}
All of this is perfectly legal to do because making IntPtr const results in data being a constant pointer to an int, NOT a pointer to a constant int. We can modify the data that p.data points to; we just can't modify p.data itself.
const IntPtr p { &a };
*p.data = 10; // This is legal
int b;
p.data = &b; // This is illegal (can't modify const member)
So how does this example apply to std::vector?
Let's add the ability to index into an IntPtr:
class IntPtr {
int* data;
public:
IntPtr() = default;
IntPtr(int size) : data(new int[size]) {}
// Even though this is a const function we can return a mutable reference
// because the stuff data points to is still mutable.
int& operator[](int index) const {
return data[index];
}
};
int main() {
const IntPtr i(100);
i[1] = 10; // This is fine
};
Even though the IntPtr is const, the underlying data is mutable because it's created by allocating an array of mutable ints. It's the same for std::vector: the underlying data is still mutable, so it's safe to const_cast it.
Related
When I store a plain int in A and perform a simple get function:
#include <iostream>
class A
{
int p;
public:
void setint(int p_x);
int getint();
};
void A::setint(int p_x) {p = p_x;} // set p (type int)
int A::getint() {return p;} // get p (type int)
int main()
{
A arr_a[5];
arr_a[0].getint();
}
it compiles and exits with code 0. However when I change int to int* and try to do the same:
#include <iostream>
class A
{
int* p;
public:
void setint(int p_x);
int getint();
};
void A::setint(int p_x) {*p = p_x;} // set int pointed to by p (type int)
int A::getint() {return *p;} // get int pointed to by p (type int)
int main()
{
A arr_a[5];
arr_a[0].getint();
}
it compiles fine but exits with code 3221225477. Why is this so and is there still a way I can store pointers in A and store A in arrays?
In your 2nd case A arr_a[5] just create a array that contains 5 A. but
for every A, the pointer is an undefined number (maybe 0x0), so *p is a undefined behavior. You should add A::A() and A::~A() to manage your pointer in your class
just like this:
#include <iostream>
class A
{
int *p;
public:
A();
~A();
void setint(int p_x);
int getint();
};
A::A() : p(new int) {}
A::~A() { delete p; }
void A::setint(int p_x) { *p = p_x; } // set int pointed to by p (type int)
int A::getint() { return *p; } // get int pointed to by p (type int)
int main()
{
A arr_a[5];
arr_a[0].getint();
}
Your program has a serious flaw. Both of your given code snippets (case 1 and case 2 in your question) have undefined behavior. Lets see how it is
Case I: Code Snippet 1
In your code snippet 1, since the data member p is a built in type and you've not initialized it, so p has a garbage value and using(accessing) this value can lead to undefined behavior which is exactly what is happening in your case.
When you wrote:
A arr_a[5];//this creates a 1D array of size 5 having elements of type `A` but the elements are default initialized
arr_a[0].getint();//this result in undefined behavior
The above statement creates a 1D array of size 5 having elements of type A. The problem is that since you have not initialized this array, its elements are default initialized which means that the value of data member p is also default initialized. But since you didn't use in-class initializers for variable p, p has garbage value and this leads to undefined behavior.
You can confirm this by looking at the output here.
Solution to Case I
You can solve this by initializing the data member p using in-class initializer as shown below:
#include <iostream>
class A
{
int p = 0;//USE IN-CLASS INITIALIZER
public:
void setint(int p_x);
int getint();
};
void A::setint(int p_x) {p = p_x;} // set p (type int)
int A::getint() {return p;} // get p (type int)
int main()
{
A arr_a[5];
std::cout<<arr_a[0].getint();//now ok because we have initilaized p
}
Case II: Code Snippet 2
In this case the only difference is that now the data member p is a pointer to int that is int*. Similar to last case the pointer variable has a garbage value which can lead to undefined behavior if you try to use it as you did inside your main function by writing:
A arr_a[5];//create a 1D array of objects `A` but the elements(A objects) are default initialized
arr_a[0].getint();//this result in undefined behavior
Solution to Case II
You can solve this by initializing the data member p using in-class initializer as shown below:
#include <iostream>
class A
{
int* p = nullptr;//USE IN-CLASS INITIALIZER
public:
void setint(int p_x);
int getint();
//other members like constructor and destructor to allocate and deallocate memory
//so that getint and setint doesn't dereference nullptr
};
void A::setint(int p_x)
{ if(p!=nullptr)// add a check here to see p isn't null
{
*p = p_x;
}
else
{
std::cout<<"WARNING: Dereferencing a nullptr can lead to UB";
}
}
int A::getint() // add a check here to see p isn't null
{ if(p!= nullptr)
{
std::cout<<"yes"<<std::endl;
return *p;
}
else
{
std::cout<<"WARNING: Dereferencing a nullptr can lead to UB";
return -1;
}
}
int main()
{
A arr_a[5];
arr_a[0].getint();//now ok(assuming `p` isn't nullptr) because we have initilaized p
}
Summary
Both of the code snippets that you gave have undefined behavior. You can solve both of them by using in-class initilaizers to initialize data member p to a default value.
class Foo
{
public:
const int a;
const int* x;
int* const y;
Foo() : a{ 0 }, y{ new int(20) } {
x = new int(10);
}
};
int main()
{
Foo f;
// f.a = 100; // cannot
f.x = new int(100);
// f.y = new int(100); // cannot
}
When const int a is defined as fields of a class,
it is called a constant member. It must be initialized in the initializer list and cannot be changed afterwards.
How about const int* x (which is the same as int const* x) and int* const y? Which one should be called as a constant member? If "constant member" is defined as field that must be initialized in the initializer list and cannot be changed afterwards, then the constant member is y rather than x. Am I wrong here?
Edit
According to IntelliSense, y is a constant member.
OK. I am sure I am not wrong. I will delete this question shortly. Thank you for your participation!
The "const int* x" is a (non-const) pointer to a const int. Since x is non-const, it need not be initialized upon construction.
Here are some examples:
class C
{
public:
C() :
const_int(1),
const_int_again(2),
const_ptr_to_non_const_int(nullptr),
const_ptr_to_const_int(nullptr)
{}
private:
const int const_int;
int const const_int_again;
const int* ptr_to_const_int; // Doesn't need initialized
int const* ptr_to_const_int_again; // Doesn't need initialized
int* const const_ptr_to_non_const_int;
const int* const const_ptr_to_const_int;
int const* const const_ptr_to_const_int_again;
};
You may find the cdecl.org website helpful.
Foo behaves like a circular iterator. Despite me being nervous about it, the code below compiles fine, but creates a run-time error. I receive the error even if I remove the consts from get_current(). Of course, I can return a pointer and it'll work; however, will I get better security returning a reference?
#include <iostream>
#include <array>
#include <memory>
class Foo
{
public:
Foo();
void next();
const int& get_current() const;
private:
std::array<std::unique_ptr<int>, 3> arr_;
unsigned i_;
};
Foo::Foo() : i_(0)
{
arr_[0] = std::unique_ptr<int>(new int(5));
arr_[1] = std::unique_ptr<int>(new int(6));
arr_[2] = std::unique_ptr<int>(new int(7));
}
void Foo::next()
{
++i_;
i_ %= 3;
}
const int& Foo::get_current() const
{
return *arr_[i_];
}
int main()
{
Foo foo;
int* p;
*p = foo.get_current();
//do something with p
std::cout << *p << std::endl;
foo.next();
*p = foo.get_current();
//do something with p
std::cout << *p << std::endl;
return 0;
}
int* p;
That's an uninitialised pointer, not pointing to anything. Dereferencing it gives undefined behaviour.
*p = foo.get_current();
That dereferences the invalid pointer. Boom!
Perhaps you want it to point to the array element
p = &foo.get_current();
or perhaps you want a copy of the array element
int n;
n = foo.get_current();
foo.get_current(); may well be returning a const reference, but after that you're attempting to take a value copy of that when assigning to *p.
Assigning to *p is what's causing you trouble as p is uninitialised. That's undefined behaviour and is manifesting itself in your case as a runtime error.
You could use code like const int& p = foo.get_current(); but do be aware that a reference can only be bound once, so you'll have to be careful with scoping.
Or, you could use std::shared_ptr<int> and make that the return type of get_current(), and strip your code entirely of bare pointers.
*p = ... You dereference int* P without having it properly initialized.
Change your code in main to
int p; // Remove *
p = foo.get_current();
//do something with p
std::cout << p << std::endl;
or if you really meant to use a pointer
const int* p;
p = &foo.get_current();
// ^ Take the address
I am reading C++ primer. I encountered the following code:
#include <iostream>
#include <string>
using namespace std;
class PrintString {
public:
PrintString(ostream &o = cout, char c = ' '): os(o), sep(c) {}
void operator() (const string &s) const { os << s << sep; }
private:
ostream &os;
char sep;
};
int main() {
const PrintString printer;
printer("ABC");
return 0;
}
This code works, but I don't know why. Below is what I think, it would be great if anyone could help to point out where I am wrong...
Here, 'printer' is a const PrintString object, so its data members are const, so 'printer.os' is a const reference to cout. Therefore, we should not be able to write to 'printer.os' since writing to cout changes it.
Thanks in advance!
The reference isn't being modified, only what it refers to. This works the same with a pointer. If you had a pointer to int data member (int*), using it in a const member function would make its type int* const. You aren't able to change the pointer itself, but you can change what it points to. For example:
struct Foo
{
int a;
int* p = &a;
void foo() const
{
p = new int; // ERROR! Not allowed to modify const pointer
*p = 100; // OK, it's a pointer to a non-const int
}
};
So when using os, you're only modifying the object that it refers to, not the reference itself.
Your confusion with const-ness is better explained with a pointer rather than a reference.
Say you have:
struct A {int data;};
struct B
{
B(A* ptr) : aPtr(ptr) {}
A* aPtr;
};
int main()
{
A a1;
A a2;
const B b(&a1);
// This makes b a const object.
// This makes b.aPtr a const pointer. That means, you cannot change where it points to
// but you can still change the value of what it points to.
b.aPtr = &a2; // Not ok.
b.aPtr->data = 10; // OK.
}
The const-ness of b.aPtr is analogous to what you would see had you used a raw pointer.
int main()
{
A a1;
A a2;
A* const aPtr1 = &a1;
// This makes aPtr1 a const pointer. That means, you cannot change where it points to
// but you can still change the value of what it points to.
aPtr1 = &a2; // Not ok.
aPtr1->data = 10; // OK.
A const* aPtr2 = &a1;
// This makes aPtr2 a pointer to a const object. That means, you can change where it points to
// but you cannot change the value of what it points to.
aPtr2 = &a2; // ok.
aPtr2->data = 10; // Not ok.
}
When it comes to references, it is similar but with a small twist. There is no such thing as a non-const reference. A reference, once initialized, cannot reference another object.
A a1;
A& ref = a1; // ref cannot be changed to reference any other object.
In your case,
const PrintString printer;
has no effect on the const-ness of the member variable PrintString::os. It continues to reference a non-const ostream. This allows you to use:
const PrintString printer;
printer("ABC");
Try to think of the reference member as a const pointer member variable, and how constness would propagate to member variables when you declare a const object of such class. What gets const is the pointer itself, not what it points to. The same semantics happen with references, The thing is that references are already const (like a const pointer variable), so it doesn't change nothing for them.
#include <iostream>
struct S {
int *p;
int *const cp;
int &ref;
};
int main() {
using namespace std;
int i = 10;
const S s{&i, &i, i};
// s.p = &i; // can't do this, s.p gets const
*s.p = 20; // changing the pointee
cout << i << endl;
// s.p = &i; // can't do this, s.p was const already, and would get if it weren't
*s.cp = 30; // changing the pointee
cout << i << endl;
// s.ref ?; // can't make a reference refer to other object
s.ref = 40; // changing the pointee
cout << i << endl;
}
Why the following code is giving me an error
Test.cpp:23:10: error: invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive] return array;
#include <iostream>
#include <stdio.h>
#define MAX_ELEMENTS 5
class CBase
{
public:
CBase()
{
for(int i = 0; i < MAX_ELEMENTS; i++)
{
array[i] = 0;
}
}
~CBase()
{
// Nothing
}
int * GetArray() const
{
return array;
}
private:
int array[MAX_ELEMENTS];
};
int main ()
{
CBase b;
return 1;
}
EDIT: I understand that I should return a const int * but then I tried something below which works fine, request to explain the reason for allowing this and not allowing the above.
#include <iostream>
#include <stdio.h>
class CBase
{
public:
CBase():ptr(NULL)
{
}
~CBase()
{
delete ptr;
}
int * ptr;
public:
int * GetPtr() const
{
return ptr;
}
};
int main ()
{
CBase b;
return 1;
}
Imagine code like this:
const CBase b;
int *array = b.GetArray();
array[0] = 5; // ooops! b changed but we declared it const!?
So as already mentioned in the comments, it does break const-correctness of your code. What you need to do is either declare GetArray() non-const, or make it return a pointer to a const int.
const int * GetArray() const
{
return array;
}
Now, code like this would not compile:
const CBase b;
const int *array = b.GetArray();
array[0] = 5;
EDIT
To answer your other question from the comments above: When you call a method that returns a value and you assign this return value to some variable, than the return value is copied to your variable and afterwards discarded. So when your calling code changes the value of this variable this has no effect on the value or variable that was initially returned. That is why a const member function can return some of the class' data members. However, when you return a pointer to a data member, than the calling code can manipulate the value of this member. Still, the pointer is copied, but even the copy points to the memory location where the class member is stored and thus you could manipulate its value.
Your method should return a const int* instead.
const int * GetArray() const
{
return array;
}
Its simple you need to either declare GetArray() non-const, or make it return a pointer to a const int.
const int * GetArray() const
{
return array;
}
The reason is that if you return non constant array then as array is returned as pointer so its value can be changed by function which gets its value so constant function indirectly results in changing the value so you need to return constant array.