why does set and get functions not work c++? - c++

i tried to change cost1 field through C class but it didn't work. how can i access and change cost1(inside M class) through C class.
i am getting null pointer error and it has shown inside setM function.
class M {
protected:
int cost1;
public:
M() {}
M(int c1) :cost1(c1) {}
int getCost1() { return cost1; }
void setCost1(int c1) { cost1 = c1; }
};
class P {
protected:
M* _m;
public:
void setM(M m) {
_m = &m;
}
M getM() {
return *_m;
}
};
class C {
protected:
std::shared_ptr<P> _p;
M _m;
public:
C(M m) {
_m = m;
}
void setP(std::shared_ptr<P> p) {
_p = p;
}
void applyM() {
std::cout << _p->getM().getCost1() << std::endl;
_p->getM().setCost1(11);
std::cout << _p->getM().getCost1() << std::endl;
}
};
int main()
{
std::shared_ptr<P> p1;
M m1(5);
std::shared_ptr<C> c1 = std::make_shared<C>(m1);
p1->setM(m1);
c1->setP(p1);
c1->applyM();
}

In the following function
void setM(M m) {
_m = &m;
}
You are passing m by value. This makes a local copy of a passed variable. Then you take address of the copied (local!) variable, which gets destroyed once the function finishes.
You need to pass either by reference or by pointer instead.

There's a problem in your class P. Its constructor takes an M object, by value. This means that when it is called, a temporary copy of the caller's M object is created and passed to it. The address of this temporary is then stored in the protected member _m. After the constructor finishes executing, the temporary copy is destroyed, the _m pointer is invalid, and accessing it will cause undefined behavior.
The solution is to pass the address that you want to store.
class P {
protected:
M* _m;
public:
void setM(M* p) {
_m = p;
}
M getM() {
return *_m;
}
};
There's also a problem in main
std::shared_ptr<P> p1;
...
p1->setM(m1);
You're dereferencing the pointer p1 without ever initializing it. Again, this is going to cause undefined behavior.

Related

Adding a string-type class member causes base class function to be called instead of child

Why does the following code print 0, but if you comment out "std::string my_string" it prints 1?
#include <stdio.h>
#include <iostream>
class A {
public:
virtual int foo() {
return 0;
}
private:
std::string my_string;
};
class B : public A {
public:
int foo() {
return 1;
}
};
int main()
{
A* a;
if (true) {
B b;
a = &b;
}
std::cout << a->foo() << std::endl;
return 0;
}
I also understand that changing std::string to std:string* also causes the code to print 1, as does removing the if-statement, though I don't understand why any of that is true.
EDIT: This seems to be due to a dangling pointer. Then what's the standard pattern in C++ to do something like this in Java:
Animal animal;
boolean isDog = false;
// get user input to set isDog
if (isDog) {
animal = new Dog();
} else {
animal = new Cat();
}
animal.makeNoise(); // Should make a Dog/Cat noise depending on value of isDog.
Problem
The program has Undefined Behaviour. b is only in scope inside the body of the if. You can't count on logical results when accessing a dangling pointer.
int main()
{
A* a;
if (true) {
B b; // b is scoped by the body of the if.
a = &b;
} // b's dead, Jim.
std::cout << a->foo() << std::endl; // a points to the dead b, an invalid object
return 0;
}
TL;DR Solution
int main()
{
std::unique_ptr<A> a; // All hail the smart pointer overlords!
if (true) {
a = std::make_unique<B>();
}
std::cout << a->foo() << std::endl;
return 0;
} // a is destroyed here and takes the B with it.
Explanation
You can point a at an object with a dynamic lifetime
int main()
{
A* a;
if (true) {
a = new B; // dynamic allocation
} // b's dead, Jim.
std::cout << a->foo() << std::endl;
delete a; // DaANGER! DANGER!
return 0;
}
Unfortunately delete a; is also undefined behaviour because A has a non-virtual destructor. Without a virtual destructor the object pointed at by a will be destroyed as an A, not as a B.
The fix for that is to give A a virtual destructor to allow it to destroy the correct instance.
class A {
public:
virtual ~A() = default;
virtual int foo() {
return 0;
}
private:
std::string my_string;
};
There is no need to modify B because once a function is declared virtual, it stays virtual for its children. Keep an eye out for final.
But it's best to avoid raw dynamic allocations, so there is one more improvement we can make: Use Smart pointers.
And that brings us back to the solution.
Documentation for std::unique_ptr
Documentation for std::make_unique

Polymorphic unique_ptr class member

I would like to have a unique_ptr class member that points to the base class, but later in the constructor through polymorphism can be changed to point to a sister class that also derives from the same base class.
While I don't get any errors in the constructor setting this polymorphism, it does not seem to work correctly, since I get error messages that my polymorphic pointer can't find a member of the sister class to which I thought the pointer was now pointing.
How do I correctly achieve polymorphism here?
class A {
int bar;
};
class B : public A {
int foo;
};
class C: public A {
C();
std::unique_ptr<A> _ptr; // changing to std::unique_ptr<B> _ptr removes the "class A has no member 'foo'" error
};
C::C() : A()
{
_ptr = std::make_unique<B>(); // no errors here
int w = _ptr->foo; // class A has no member 'foo'
}
When you assign
_ptr = std::make_unique<B>();
This works because B is a derived class of A, however _ptr is still a unique_ptr to the base class. You can't change the type of a variable after it's declared.
So what are your options?
Because you know that _ptr stores a pointer to the derived class B, you can do a cast after dereferencing it:
_ptr = std::make_unique<B>();
// derefence the pointer, and cast the reference to `B&`.
B& reference_to_sister = (B&)(*_ptr);
int w = reference_to_sister.foo;
If you take this approach, you'll have to somehow keep track of which derived class is in _ptr, or you'll run the risk of running into bugs.
Alternatively, if you're using C++17, you can use std::variant:
class C : public A {
void initialize(A& a) {
// Do stuff if it's the base class
}
void initialize(B& b) {
// Do different stuff if it's derived
int w = b.foo;
}
C() {
_ptr = std::make_unique<B>(); // This works
// This takes the pointer, and calls 'initialize'
auto initialize_func = [&](auto& ptr) { initialize(*ptr); };
// This will call 'initialize(A&)' if it contains A,
// and it'll call 'initialize(B&)' if it contains B
std::visit(initialize_func, _ptr);
}
std::variant<std::unique_ptr<A>, std::unique_ptr<B>> _ptr;
};
In fact, if you use std::variant this will work even if A and B are completely unrelated classes.
Here's another short variant example
#include <variant>
#include <string>
#include <iostream>
void print(std::string& s) {
std::cout << "String: " << s << '\n';
}
void print(int i) {
std::cout << "Int: " << i << '\n';
}
void print_either(std::variant<std::string, int>& v) {
// This calls `print(std::string&) if v contained a string
// And it calls `print(int)` if v contained an int
std::visit([](auto& val) { print(val); }, v);
}
int main() {
// v is empty right now
std::variant<std::string, int> v;
// Put a string in v:
v = std::string("Hello, world");
print_either(v); //Prints "String: Hello, world"
// Put an int in v:
v = 13;
print_either(v); //Prints "Int: 13"
}

C++ object creation without constructor call

In c++, creating an object without calling a constructor like this. Does this count as defined behavior? (Disregard the unfreed heap memory)
class Foo {
public:
int *ptr;
int a, b;
Foo() { ptr = new int{}; }
~Foo() { delete ptr; }
void set(int a_, int b_) {a = a_; b = b_;}
void print() { std::cout << *ptr << a << b; }
};
int main() {
char *array[sizeof(Foo)]; // note the pointer
*((int**)array) = new int{3};
((Foo*)array)->set(1, 2);
((Foo*)array)->print(); // 312
}
You are building a memory block that can hold the data for the object, but that is not an object.
An object consist of potentially a lot more than that slice of memory, and what exactly more is up to the compiler. It could be nothing more, but it could be a lot of other things.

STL Container with iterator and inheritance

Here is a sample C++ question to find out the outcome.
#include <iostream>
#include <vector>
class A
{
public:
A(int n = 0) : m_n(n) { }
public:
virtual int f() const { return m_n; }
virtual ~A() { }
protected:
int m_n;
};
class B
: public A
{
public:
B(int n = 0) : A(n) { }
public:
virtual int f() const { return m_n + 1; }
};
int main()
{
const A a(1);
const B b(3);
const A *x[2] = { &a, &b };
typedef std::vector<A> V;
V y({ a, b });
V::const_iterator i = y.begin();
std::cout << x[0]->f() << x[1]->f()
<< i->f() << (i + 1)->f() << std::endl;
return 0;
}
The output I expected was "1 4 1 4" but the correct answer is "1 4 1 3".
From above,
x[0]->f()
i.e., x[0] is nothing but a pointer to an object of type A and calling f() returns 1.
x[1]->f()
i.e., x[1] is nothing but a pointer to an object of type A (base class pointer pointing to derived class object) and calls derived class f() that returns (3 + 1) = 4
I am not sure how this behaves when we add the objects a and b into a vector container and iterating them through const_iterator with inheritance
i->f()
I can understand this as i is just a pointer to the first element i.e., object a.
But what will happen here?
(i + 1)->f()
My understanding is that it points to the next element in the sequence i.e., object b and calling f() through derived class pointer should call its member function rather than base class one's?
The vector y contains two objects of type A. Not type B. When it is constructed, it makes copies of a and b, slicing b as it does so. So (i + 1)->f() calls A::f() on that copy of the A portion of b, giving 3.

How do I avoid a segmentation fault when calling a method on a shared_ptr?

Sample program:
#include <memory>
#include <iostream>
class D;
class C {
public:
C();
void callD();
void replaceD(D* d);
private:
std::shared_ptr<D> d;
};
class D {
public:
D(C* c);
void call();
private:
virtual void print();
C* c;
};
C::C() : d(new D(this)) {}
void C::callD() {
d->call();
}
void C::replaceD(D* d) {
this->d = std::shared_ptr<D>(d);
}
D::D(C* c) : c(c) {}
void D::call() {
c->replaceD(new D(c));
print();
}
void D::print() {
std::cout << "Hello, World!" << std::endl;
}
int main(void) {
auto c = new C();
c->callD();
return 0;
}
(With gcc: g++ -std=c++11 tmp.cpp -o tmp && ./tmp)
What happens:
C::callD() calls d->call()
D::call() calls c->replaceD()
C::replaceD() reassigns the C’s d pointer, causing the old d to be deleted
D::call() attempts to call the virtual method print() – but the current d instance was deleted by C::replaceD()!
Segmentation fault
Workaround: Insert auto d_ = d; in C::callD() so that the shared_ptr isn’t deleted until C::callD() finishes. But that looks suspiciously like an optimizeable unused variable (though g++ doesn’t seem to remove it even on -O3).
Shouldn’t the refcount be increased on any ptr->method() call until method() returns, to avoid this problem where method’s this is prematurely deleted?
Shouldn’t the refcount be increased on any ptr->method() call until method() returns, to avoid this problem where method’s this is prematurely deleted?
No. The semantics of std::shared_ptr are strictly pointer references, not function calls. The object needs to be referenced in another object or on the stack. You're deleting the reference.
void C::replaceD(D* d) {
this->d = std::shared_ptr<D>(d);
}
This code might as well be delete this instead of c->replaceD(new D(c)).
void D::call() {
c->replaceD(new D(c));
print();
}
I'm not sure what you're trying to do, but if you need to keep the reference to D alive, you can rewrite C::replaceD.
std::shared_ptr<D> C::replaceD(D* d) {
std::shared_ptr<D> old = this->d;
this->d = std::shared_ptr<D>(d);
return old;
}
Then rewrite D::call.
void D::call() {
std::shared_ptr<D> prev = c->replaceD(new D(c));
prev->print();
}
You can pass a std::shared_ptr reference into C::replaceD that can be given ownership of Cs current D before it is deleted.
void C::replaceD(D* d, std::shared_ptr<D>& tmp)
{
tmp.swap(this->d);
this->d = std::shared_ptr<D>(d);
}
and then D can keep itself alive until the end of the D::call
void D::call()
{
std::shared_ptr<D> tmp {};
c->replaceD(new D(c), tmp);
print();
}
I have to say though that your code seems poorly designed to me, and I would first consider if there's not a better design that would avoid this issue entirely.