Let's say I have a class PArr which stores an array of various classes StringWrapper, FloatWrapper, IntWrapper- all of them derive from a base class Wrapper.
It's PArr class:
class PArr{
private:
Wrapper** _wrappers;
int _size;
public:
PArr(int size);
Wrapper& operator[](int index);
};
And here's the implementation of this class
#include "PArr.h"
PArr::PArr(int size){
_size = size;
_wrappers = new Wrapper*[_size];
}
Wrapper& PArr::operator[](int index){
return _wrappers[index];
}
And here's the class for example FloatWrapper
#pragma once
#include "Wrapper.h"
class FloatWrapper : public Wrapper{
private:
float* _floatPtr;
public:
FloatWrapper(float number);
};
My main method looks like this:
int main() {
PArr a(3);
a[0] = new FloatWrapper(0.1);
}
I get an error:
"no match for 'operator=' (operand types are 'Wrapper' and 'FloatWrapper*')
What am I doing wrong?
At the risk of stating the obvious: your operator[] is returning a Wrapper&, which is a reference type, and you're trying to assign a Wrapper*, a pointer type to it. This ain't gonna work.
If you rewrote your main function slightly, say like this:
int main()
{
PArr a(3);
auto k = a[0];
}
you might find the error more informative:
main.cpp: In member function 'Wrapper& PArr::operator[](int)':
main.cpp:27:27: error: invalid initialization of reference of type 'Wrapper&' from expression of type 'Wrapper*'
return _wrappers[index];
~~~~~~~~~~~~~~~^
You might try something like this instead:
Wrapper*& PArr::operator[](int index)
{
return _wrappers[index];
}
which does compile and let your original main run, at least for me. I make no further promises.
Finally, this answer isn't the place to go into the deeper problems with your code (use smart pointers, use standard library container classes like array or vector, consider things like std::any or std::variant), but, y'know, you should read up on those things.
Related
I have the following code (live on Coliru):
// untouchable extern library .hpp file
typedef union ExternLibraryUnion
{
int a;
float b;
}ExternLibraryUnion;
// my code
#include <iostream>
class Container{
public:
Container() : m_union(NULL) {};
~Container(){
if(m_union){
delete m_union;
}
}
void init(){
m_union = new ExternLibraryUnion();
}
ExternLibraryUnion* get_union(){
return m_union;
}
private:
ExternLibraryUnion* m_union;
};
class Master{
public:
Master() : m_union(NULL) {
m_container.init();
};
~Master(){
if(m_union){
delete static_cast<ExternLibraryUnion*>(m_union);
}
}
void load(){
}
void set(int i){
m_union = m_container.get_union();
m_union->a = i;
}
void* get_union(){
return m_union;
}
private:
void* m_union;
Container m_container;
};
class Worker{
public:
Worker() : m_extern_library_union(NULL) {};
~Worker(){
if (m_extern_library_union){
delete m_extern_library_union;
}
}
void load(Master& master){
m_extern_library_union = reinterpret_cast<ExternLibraryUnion*>(master.get_union());
}
int get_int(){
return m_extern_library_union->a;
}
private:
ExternLibraryUnion* m_extern_library_union;
};
int main()
{
Master master;
master.set(3);
Worker worker;
worker.load(master);
std::cout << worker.get_int() << std::endl;
}
The code produces:
main.cpp: In member function 'void Master::set(int)':
main.cpp:55:16: error: 'void*' is not a pointer-to-object type
m_union->a = i;
^~
In an extern library, a union ExternLibraryUnion is defined which I'm using inside my own code. My problem, which I can't get my head around, is in the set method of class Master. The Master member void* m_union should point to the union stored inside the member Container m_container. As I'm setting the m_union = m_container.get_union() the compiler should be able to know that I'm getting a ExternLibraryUnion* back from the get_union() method call. So I don't quite the error arising from the assignment m_union->a = i. Sure, a void* has no type, but I assigned it a pointer of the precise type ExternLibraryUnion.
Let's also say I can not touch the Container m_container object directly. I need to make the assigned through the void* m_union pointer.
Any help is highly appreciated!
The compiler has no clue what m_union might actually be pointing at. You declared it as a void * so the compiler believes you, it has no choice. And that's all it knows, so m_union->a has to be flagged as an error, because ->a has no meaning to the compiler here.
To put it another way, RTTI aside, pointers don't 'know' what they're pointing at. The compiler only knows how the pointer was declared.
I don't know what else to say, it's really that simple. I don't like having to say this, but looking at the code as a whole, it looks like a complete mess. Who wrote it?
[Edit] And what Jeffrey said will indeed fix it, but that's not what you asked.
You need to change
private:
void* m_union;
to
private:
ExternLibraryUnion* m_union;
at line 63. As your code stand, you upcast the pointer to void*, and then, at the next line the compiler can't know the pointed type.
If you can't change the type, you can static_cast the void pointer to an
ExternLibraryUnion*. Then use that to access. Since you know the type, the static_cast could be "acceptable". But not the nicest design by any measure.
This is the header for the class in question. The idea is to have a class that acts like a pointer to access the heap. So far I have this.
#ifndef PTR_H_
#define PTR_H_
template < typename T >
class ptr
{
private:
T * p;
public:
ptr< T >()
: p(new T)
{
}
ptr< T >(ptr & pt)
: p(pt.p)
{
}
~ptr()
{
delete [] p;
}
};
#endif
I've kept them all inline for simplicity sake but all said and done, I'm running into a wall. I'm trying to have it access the heap right away and I'm already getting the non-scalar type issue.
Here's the main.
#include <iostream>
#include "ptr.h"
int main()
{
ptr< int > p = new int;
return 0;
}
My question is pretty simple since I'm still new C++ OOP, what exactly did I do wrong and what are some guidelines I need to know to avoid this issue in the first place?
Your error "invalid conversion from *int to a nonscalar type 'ptr< int >'" is caused by your declaration ptr<int> p = new int. ptr<int> p is already enough. If you are not doing this to learn, you should use std::unique_ptr and std::shared_ptr instead.
If you want to make your declaration possible, you will have to make a custom constructor like this ptr<T>(T* t) : p(t) { }.
Compiling my code that contains this class:
class Dessin
{
private:
vector<Figures*>T;
public:
void ajouteFigure(const Figures& f) const
{
for(auto element: T)
{
T.push_back(f);
}
}
};
yields an error:
[Error] no matching function for call to
'std::vector::push_back(const Figures&) const'
This is what I'm supposed to do in the main()
Dessin s;
s.ajouteFigure(Cercle(1.1));
Why wouldn't this work?
Assuming Cercle is a class name, you're trying to push a value where a pointer is expected.
To "fix" the error you should change your ajouteFigure prototype to accept Figures pointers and non-const this:
void ajouteFigure(Figures* f)
Then you should call it passing a pointer to a Figures object, i.e. created with a new expression:
s.ajouteFigure(new Cercle(1.1));
That being said, this code seems pointless. You're adding the pointer as many times as you have elements in the vector (which is always 0 in the example you provided).
Using raw pointers is also unadvised, you should use smart pointers like std::unique_ptr, although that would break the current code.
Consider this, less improper, example:
class Dessin
{
private:
vector<unique_ptr<Figures>> T;
public:
void ajouteFigure(unique_ptr<Figures> f)
{
T.push_back(move(f)); // just once
}
};
and at the call site:
Dessin s;
s.ajouteFigure(make_unique<Cercle>(1.1)); // C++≥14
or, if you can't use C++14:
Dessin s;
s.ajouteFigure(unique_ptr<Figures>(new Cercle{1.1}));
Just to add to this, I think you would be better to make it a template function and create the right object inside the function with arguments to the constructor passed as function parameters.
This way you don't have to create a std::unique_ptr or use new every time you call the function.
Here's a basic implementation:
class Dessin{
public:
template<typename T, typename ... Args>
void ajouteFigure(Args &&... args){
figures.emplace_back(new T(std::forward<Args>(args)...));
}
private:
std::vector<std::unique_ptr<Figures>> figures;
};
Then using the class is less error-prone:
int main(){
Dessin d;
d.ajouteFigure<Cercle>(1.1);
}
I want to recombine a (complex) member attribute of two agents and put it in a new agent. It's a vector of numbers, every second value is taken from agent1, the rest from agent2. The problem is, I want to be able to exchange the implementation of my numberList, maybe another numberListInt2 using integers or like in my example using floats:
#include <vector>
using namespace std;
class NumberList {
};
class NumberListInt : public NumberList {
vector<int> number_list {1,2,3};
};
class NumberListFloat : public NumberList {
vector<float> number_list {1.2f,2.5f,30.0f};
};
class Agent {
NumberList* numbers;
public:
Agent();
Agent(NumberList* numbers) {
numbers = numberList*
}
~Agent() {
delete numbers;
}
NumberList* recombine(Agent& other) {
NumberList* new_number_list;
if(true) // a boolean config value
new_number_list = new NumberListInt();
else
new_number_list = new NumberListFloat();
for(unsigned int i=0;i<3;i++) {
if(i%2)
new_number_list[i] = other.get_number_list()[i];
else
new_number_list[i] = numbers[i];
}
return new_number_list;
}
NumberList* get_number_list() {
return numbers;
}
};
int main ()
{
Agent agent;
Agent agent2;
Agent agent3(agent.recombine(agent2));
return 0;
}
My questions:
How to implement the operator [] of NumberList?
Is there a better way than using a pointer for the polymorphism?
Do I free the memory correctly?
Thanks in advice!
The problem is that operator[] shall return a reference to an item of the NumberList. But you don't know the type of the numbers, when you're in the parent class. So you won't be able to define this operator in a polymorphic manner (unless you define somehow a polymorphic item).
To benefit from polymorphism you have to use references or pointers. In your case the pointers are a good alternative. However you have to clarify their use in the constructors. I assume that the copy constructor should copy the object and not reuse the list of the original agent.
No, because you have not defined a virtual destructor for NumberList. And when you recombine() you return a newly allocated list. So the caller has to delete the returned object. That's very dangerous: if he forget, memory will leak. You'd better consider opting for shared_ptr to avoid leaking.
It's not clear if you need dynamic change of the NumberList type at runtime. If you don't, a safer approach would be to use templates
With templates it would look like:
template <class T>
class NumberList {
vector<T> number_list;
T& operator[] (size_t i) { return numberlist[i]; } // may be add boundary check ?
};
template <class T>
class Agent {
NumberList<T> numbers; // No more pointer, but directly the object
....
};
Say I have a simple class like this
class Foo
{
public:
void foo()const
{
str[5] = 'x';
obj->changeTheWorld();
x = 4;
y.get() = 5;
obj2->changeTheWorld();
}
private:
char *str; //some referenced data, not owned by Foo
ComplexObj *obj; //some referenced data, not owned by Foo
int &x; //references as well
//wrapped reference, but has a "T& get()const"
std::reference_wrapper<int> y;
//an occasionally useful pointer wrapper for complex memory cases
//but has a "T* get()const"
std::shared_ptr<ComplexObj> obj2;
};
This is valid because in the const method, its just the pointer itself that becomes const, not the data it points to. However in many cases that is not what I desired and I want a compile error if a const method tries to change these members contents (either directly or by calling a non-const method on that member).
Is there a standard solution to this?
I think some kind of wrapper class should be able to achieve this, and should also be something the compiler optimises out, although haven't sat down to try and design such a thing to cover all cases giving say a strong_const<char*> str and strong_const<int&> (also not sure on a good name...).
Well, neither std::reference_wrapper nor std::shared_ptr do not provide const propagation, so they are not more "const-strict" than regular pointer.
I'd recommend to make your own const propagation class (I am not sure - maybe something similar is already provided by boost - please let me know in comments)
My proposition is this class:
#include <memory> // for pointer_traits
template <typename Pointer>
class ConstPropagatePointer
{
public:
using element_type = typename std::pointer_traits<Pointer>::element_type;
using pointer = typename std::pointer_traits<Pointer>::pointer;
using const_pointer = element_type const * const;
using reference = element_type&;
using const_reference = element_type const&;
ConstPropagatePointer(Pointer ptr) : ptr(ptr) {}
pointer operator -> ()
{
return &(*ptr);
}
const_pointer operator -> () const
{
return &(*ptr);
}
reference operator * ()
{
return *ptr;
}
const_reference operator * () const
{
return *ptr;
}
private:
Pointer ptr;
};
So that will work for you:
class Foo
{
public:
private:
ConstPropagatedPointer<char*> str;
ConstPropagatedPointer<ComplexObj*> obj;
ConstPropagatedPointer<std::shared_ptr<ComplexObj>> obj2;
};