User-defined object containing pointer breaks when stored in array - c++

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.

Related

I'm Getting error while trying to delete heap allocated int

#include <iostream>
class TEST
{
public:
int* a = new int;
TEST(int x)
: a(&x)
{
}
~TEST()
{
delete a;
}
};
int main()
{
{
int i = 2;
TEST T(i);
}
std::cin.get();
}
I tried to heap allocate integer in a TEST class and then delete it but
when I'm calling delete a in TEST class destructor I'm getting error in file called delete_scalar.cpp and I have no idea what does it mean and how to fix it. Where's the problem?
TEST(int x)
: a(&x)
{
}
This code is causing undefined behavior. It is making a point at a local int that goes out of scope immediately afterwards, leaving a as a dangling pointer to invalid memory. a is never pointing at dynamic memory (the new expression in the declaration of a is ignored when a is initialized in the constructor's member initialization list). That is why the delete fails later.
To do what you are attempting, you need to either:
move the new into the initialization list, eg:
int* a = nullptr;
TEST(int x)
: a(new int(x))
{
}
Or, if you want to keep using new in the declaration of a, then you can assign the value of that int from inside the constructor's body instead of in its initialization list, eg:
int* a = new int;
TEST(int x)
{
*a = x;
}

subscript operator overload returning pointer and assigning value to it

I got a situation where I have a class B having a protected array of A*. I have an another function A* func() which returns pointers to A objects stored in some other container. (for example std::map<int,A*> or A* arr[]).
For example the following implementation:
#include <iostream>
using namespace std;
class A {
public:
A(int _x): x(_x){};
int x;
};
class B{
public:
B() {
this->arr[0] = new A(1);
this->arr[1] = new A(2);
}
// use idx to index in to the internal array and return A*
A*& operator[](int idx); // return reference to pointer to avoid not a lvalue error
private:
// class B has this private array of A*
A* arr[2];
};
A*& B::operator[](int idx) {
return this->arr[idx]; // 0<=idx<=1
}
A* arr[2];
A* func(int x) {
return arr[x]; // 0<=idx<=1
}
int main() {
arr[0] = new A(12);
arr[1] = new A(14);
B b;
b[1] = func(1); // assign a new value
cout << b[1]->x << endl; // prints 14
}
// memory leaks of this program can be removed using `std::shared_ptr<A>` in the place of `A*`.
In this example, operator[]() returns a reference to a pointer, because returning pointer becomes rvalue and I will not be able to assign value to it (i.e. b[1] = func(1)).
My question is, is this reference to pointer returning recommended (other ways to achieve this )? or is this perfectly fine with raw pointers as well as with shared_ptr?
Thank You!

Modifying element of const std::vector<T> via const_cast

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.

Returning a reference to an element of a class member container

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

Returning a pointer to an member array from const member function

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.