I have two classes
class A { C* c; }
class B { D* d; }
and find I need to construct a std::vector whose elements are either A or B (with the sequence decided at run time. So I constructed a polymorphic
class Poly {
int oType;
void* oPtr;
}
as well as constructor
Poly::Poly(int type)
{
if (type == 1) oPtr = new (A*) oPtr();
if (type == 2) oPtr = new (B*) oPtr();
oType = type;
}
along with a similarly structured destructor. Now
std::vector<Poly*> test;
works. However, I am having trouble accessing the subobjects.
I tried
if (test->oType == 1) test->oPtr->a;
if (test->oType == 1) test->(A*)oPtr->a;
if (test->oType == 1) (A*)(test->oPtr)->a;
all giving me the compiler error:
'void*' is not a pointer-to-object type
How do I convince the compiler that it's OK to reference a, if I know that the type of oPtr is A*?
How do I convince the compiler that it's OK to reference a, if I know
that the type of oPtr is A*?
Strictly I think the answer to that is: ((A*)(test->oPtr))->a. The better way to do that in C++ uses the cast operator: static_cast<A*>(test->oPtr)->a
HOWEVER This is not typically how this problem is addressed in c++. So I have provided a more usual approach that you may find useful:
class Poly
{
public:
virtual ~Poly() {}
virtual void do_something() = 0; // each sub-type has its own version of this
};
class A: public Poly
{
public:
void do_something() /* override */ // c++11 only
{
std::cout << "Doing something A specific\n";
}
};
class B: public Poly
{
public:
void do_something() /* override */ // c++11 only
{
std::cout << "Doing something B specific\n";
}
};
int main()
{
std::vector<Poly*> polys;
// create data structure
polys.push_back(new A);
polys.push_back(new A);
polys.push_back(new B);
polys.push_back(new A);
// use objects polymorphically
for(size_t i = 0; i < polys.size(); ++i)
polys[i]->do_something();
// clean up memory (consider using 'smart pointers')
for(size_t i = 0; i < polys.size(); ++i)
delete polys[i];
}
As others mentioned, the polymorphic way is to use virtual functions.
Here is an implementation using smart pointers. The creator class is responsible for creating the Poly object we are asking for. This isolates the creation to one class.
Note that there are more sophisticated ways of doing this. The goal here is to show, more or less, how it would be done using C++.
#include <vector>
#include <memory>
#include <iostream>
class Poly
{
public:
virtual void Test() = 0;
};
typedef std::unique_ptr<Poly> PolyPtr;
class A : public Poly
{
public:
void Test() { std::cout << "Test for A" << "\n"; }
};
class B : public Poly
{
public:
void Test() { std::cout << "Test for B" << "\n"; }
};
class PolyCreator
{
public:
PolyPtr CreatePolyObject(int oType)
{
switch( oType )
{
case 1:
return PolyPtr(new A());
case 2:
return PolyPtr(new B());
}
throw "Could not find type in list";
}
};
int main()
{
PolyCreator pCreator;
std::vector<PolyPtr> PolyPtrVect;
// create objects
PolyPtrVect.push_back(pCreator.CreatePolyObject(1));
PolyPtrVect.push_back(pCreator.CreatePolyObject(2));
// call Test functions for each
std::vector<PolyPtr>::iterator it = PolyPtrVect.begin();
while ( it != PolyPtrVect.end())
{
(*it)->Test();
++it;
}
}
Output:
Test for A
Test for B
Note
There is only one if() statement that is isolated to the PolyCreator class.
There are no memory leaks due to usage of std::unique_ptr.
Poly is an abstract class. All derived classes must implement the Test function.
Related
Description:
I was trying to create a class object based on the user input. I tried to store its reference with a pointer and use it at a later stage. A similar idea with the variable type late in Dart language. Is it possible to have this operation in CPP and if so, what kind of variable type should I use?
Code:
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
class foo {
public:
T item;
foo(){}
};
class bar_1 {
public:
int value = 99;
void randomise();
};
class bar_2{
public:
string value = "init";
void randomise();
};
int main()
{
int i;
cin >> i;
void* ptr;
if (i > 0) {
ptr = new foo<bar_1>;
}
else {
ptr = new foo<bar_2>;
}
// maybe run the function to randomise 'value'
// ptr->item.randomise();
cout << ptr->item.value;
return 0;
}
Error:
cout << ptr->item.value;
| ^~
error: ‘void*’ is not a pointer-to-object type
Edit 1: I am not allowed to modify the class bar atm, aschepler’s #1 solution fits the best.
Edit 2: I am revisiting this problem. I found the most straightforward method is to dump using a template and convert them into a class hierarchy structure, with 'bar 1' and 'bar 2' being the offspring classes of 'foo'. So, if a pointer to class 'foo' exists in the scope, it may be assigned as a pointer to either a 'bar 1' or a 'bar 2' object.
Updated code: (class hierarchy)
#include <iostream>
using namespace std;
class foo {
public:
// common function going to be overrided by children classes
virtual void randomize() {
}
};
class bar_1: public foo {
public:
int value = 99;
// bar_1 version of randomize()
void randomize() override {
cout << "Turning int from " << value << " to a new int\n";
value++; // increment by 1
}
};
class bar_2: public foo {
public:
std::string value = "something";
// bar_2 version of randomize()
void randomize() override {
cout << "Turning string from " << value << " to a new string\n";
value = "new thing"; // diff string
}
};
int main()
{
int type = 1; // e.g type can variate from 1 to 10
foo* ptr;
switch(type)
{
case 1:
ptr = new bar_1();
break;
case 2:
ptr = new bar_2();
break;
// case 3:
// ...
default:
cout << "Invalid type\n";
break;
}
ptr->randomize();
return 0;
}
Updated code: (keep using template class)
#include <iostream>
#include <variant>
#include <optional>
using namespace std;
template <typename T>
class foo {
public:
T item; // an object either bar_1 or bar_2
foo(T _item) : item(_item) {} // constructor assigning in-class variable "item"
T getItem() // template function returning the in-class variable item
{
return item;
}
};
class bar_1 {
public:
int value = 99;
};
class bar_2{
public:
string value = "str";
};
int main()
{
int i = 0; // some value
optional<variant<foo<bar_1>, foo<bar_2>>> foo_foo;
if (i > 0) {
foo_foo = foo(bar_1());
}
if (i < 0) {
foo_foo = foo(bar_2());
}
// using visit
// if i > 0, it gives 99 as foo.item has type bar_1, which has an int value 99
// if i < 0, it gives "str" as foo.item has type bar_2, which has an string value "str"
// if i == 0, it gives 0 as the optioanl variable foo_foo contains no values
visit([](auto &_foo) { cout << _foo.getItem().value; }, *foo_foo);
return 0;
}
Definitely not in the way dynamic languages like JS and (AFAIK) Dart allow.
C++ is statically typed, so by the time you write cout << ptr->item.value in a (non-template) function, the types of item and value must be known. No way conflating unrelated types like int and string at runtime.
However, you may be able to use inheritance to achieve desired effect. A pointer to an object may always be converted to a pointer to its (accessible, i.e. public in most cases) base, and accessed like that base—but the object will retain its actual type, and all virtual methods of base will act on that type, and can be overriden:
class foo {
public:
virtual ~foo() = default; // optional but highly recommended
virtual randomize() {
// the default implementation
}
// or: virtual randomize() = 0; // if you want ALL subclasses to override it
};
class bar_1: public foo {
public:
int value = 99;
void randomize() override {
// the bar_1-specific implementation
}
};
class bar_2: public foo {
public:
std::string value = "something";
void randomize() override {
// the bar_2-specific implementation
}
};
...
foo *obj = new bar_1(); // create an object of type bar_1, but treat it as foo
obj->randomize(); // will call bar_1::randomize as obj points to an object of type bar_1
// obj->value = 42; // won’t work: value is not a member of foo
delete obj;
obj = new bar_2(); // now, create an object of type bar_2, but treat it as foo again
obj->randomize(); // will call bar_2::randomize as obj now points to an object of type bar_2
delete obj;
Or with smart pointers (highly recommended):
std::unique_ptr<foo> obj = std::make_unique<bar_1>()
// std::unique_ptr<foo> obj{new bar_1()}; // if you can’t afford C++17
obj->randomize();
obj = std::make_unique<bar_2>();
// obj.reset(new bar_2()); // if you can’t afford C++17
obj->randomize();
As comments have pointed out, you can't use a pointer to a variable declared in a block after the end of that block. You could fix this with a std::unique_ptr, or possibly std::optional.
Here are two possible solutions.
#1: A std::variant<foo<bar_1>, foo<bar_2>> can hold an object of either type, without requiring any changes to your existing classes (and without requiring any dynamic allocations). Then we can use std::visit to do things on whichever object it contains.
#include <variant>
#include <optional>
int main()
{
int i;
std::cin >> i;
std::optional<std::variant<foo<bar_1>, foo<bar_2>>> the_foo;
if (i > 0) {
the_foo = foo<bar_1>{};
}
else {
the_foo = foo<bar_2>{};
}
// run the function to randomise 'value'
std::visit([](auto &foo) { foo.item.randomise(); }, *the_foo);
std::visit([](auto &foo) { std::cout << foo.item.value; }, *the_foo);
return 0;
}
#2 If you can change the classes, notice that bar_1 and bar_2 both contain some common operations "randomise item" and "print item". So we can create an interface allowing polymorphic use of those operations without knowing the actual type. This is also more easily extensible if you add additional similar classes later.
class IBar {
public:
virtual ~IBar() = default;
virtual void print(std::ostream& os) const = 0;
virtual void randomise() = 0;
};
class bar_1 : public IBar {
public:
int value;
void print(std::ostream& os) const override
{ os << value; }
void randomise() override;
};
class bar_2 : public IBar {
public:
std::string value;
void print(std::ostream& os) const override
{ os << value; }
void randomise() override;
};
Now foo doesn't even need to be a template. It can just use an interface pointer instead of a member of the actual type:
#include <memory>
class foo {
public:
std::unique_ptr<IBar> pItem;
explicit foo(std::unique_ptr<IBar> p) : pItem(std::move(p)) {}
foo() = default;
};
int main()
{
int i;
std::cin >> i;
foo the_foo;
if (i > 0) {
the_foo.pItem = std::make_unique<foo<bar_1>>();
}
else {
the_foo.pItem = std::make_unique<foo<bar_2>>();
}
// run the function to randomise 'value'
the_foo.pItem->randomise();
the_foo.pItem->print(std::cout);
return 0;
}
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
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"
}
I have seen Is it possible to replace a method at runtime in C/C++?, but I am something of a C++ newbie, and so cannot yet see how (and if) it applies to my case, so I'd like to try asking with my example.
In the example below, assume classes MyBase, A1, and A2, come from some library, and I'm using them in my main:
// g++ -std=c++11 -o test.exe test.cpp
#include <iostream>
using namespace std;
class MyBase {
public:
MyBase() { }
~MyBase() { }
int getBaseVal(int inval) {
return inval + 5;
}
};
class A1 : public MyBase {
public:
A1() : MyBase() { }
~A1() { }
int getVal(int inval) {
return getBaseVal(inval) + 10;
}
};
class A2 : public A1 {
public:
A2() : A1() { }
~A2() { }
int getVal(int inval) {
return getBaseVal(inval) + 20;
}
};
int main() {
A1 _a1 = A1(); A2 _a2 = A2();
cout << "A1 for 10: " << _a1.getVal(10) << endl;
cout << "A2 for 10: " << _a2.getVal(10) << endl;
return 0;
}
Now, let's say I would like to leave this library untouched, but I've discovered a bug in MyBase::getBaseVal, in that the right value returned should be inval + 6; instead of the given inval + 5;. So, nothing complicated, but access to the input arguments of the method is needed.
So do I have an option, to define a new function, say:
int getNewBaseVal(int inval) {
return inval + 6;
}
.. and then somehow "replace" the old MyBase::getBaseVal:
On a class level (that is, MyBase would be "patched"), such that all subsequent instantiations of A1 and A2 will ultimately use the getNewBaseVal when their getVals are called;
On an object level, such that only a particular instantiation of, say, A1 as object (like _a1) would ultimately use the getNewBaseVal when its getVal is called;
... by writing some code at the start of the main function?
The long and short of it is that you can't; at least, not within the given constraints (which rule out various sub-optimal hacks, such as Andrey's!).
Get the library author to fix the bug, and build your own forked and fixed version of it in the meantime.
I'd like to suggest you to create an adapter class and use it as the base class for A and B
class MyBaseAdaptor: public MyBase
{
using MyBase::MyBase; // Inherit constructors
public:
int getBaseVal(int inval) // Hide the old method
{
// your code here
}
};
But it is better to fix the library.
Really bad title, couldn't think of how to word it, sorry.
So say I had the following code:
class A {
virtual int getSize() {
return 0;
}
}
class B : public A {
int getSize() {
return 32;
}
}
void doStuff(A a) {
std::cout << a.getSize() << std::endl;
}
int main() {
B b;
doStuff(b);
}
It would print out 0, however I want it to print out 32. In other words, I want to pass it the class and it prints out that classes function, so I could create a class C, where the size is 64, and if I pass that C instance to the doStuff function, I want it to print 64.
Is there any way I can do this in C++, would I have to use templates or some fancy C++ feature I don't know about?
A one-byte patch:
void doStuff(A &a) {
std::cout << a.getSize() << std::endl;
}
Your version takes the argument by value, which means that the function makes a copy of b (a copy which is an A) and then calls the copy's getSize(). In this version, the function takes the argument by reference, and calls b's own getSize(), which is B::getSize().
You should use pointers, or even better: smart pointers! That way, the function of the runtime type gets called. It's a basic example of polymorhpism. If you want to avoid pointers, Beta's slicing approach is equally valid.
#include <iostream>
#include <memory>
class A {
virtual int getSize() {
return 0;
}
}
class B : public A {
virtual int getSize() {
return 32;
}
}
void doStuff(std::shared_ptr<A> a) {
std::cout << a->getSize() << std::endl;
}
int main() {
std::shared_ptr<A> b(new B());
doStuff(b); // Will output '32'.
}
This should correctly call the function as implemented by B.
Slicing the object is one approach, and in addition I think you're asking for, I think, a pretty straightforward use of polymorphism in C++. http://www.cplusplus.com/doc/tutorial/polymorphism/
That's almost immediately applicable, just call your class A Shape, and B and C could be Square and Triangle. Your DoStuff function could take a pointer to a Shape, then you can pass it a triangle or a square, and when you deference the Shape in the function, it will call the correct function.
So you'd have (also you need to make the members public, I think):
class A {
public:
virtual int getSize() {
return 0;
}
};
class B : public A {
public:
int getSize() {
return 32;
}
};
void doStuff(A* a) {
std::cout << a->getSize() << std::endl;
}
int main() {
B b;
doStuff(&b);
}