I already posted another question regarding structs versus classes. I believe I got good feedback, so I went back to work on my code.
Here is what confused me (maybe it is childish ;) )
I need to Have a class (or struct doesnt really mind) that is called cPoint, and it just defines a Point in space with X, Y coordinates.
from it all shape-classes will derive. So for cRectangle, that is a class for rectangles, the cPoint will be used as a base element.
To instantiate a Rectangle, the caller must provide the coordinates of 2 points, that will be the rectangles UpperLeft & LoowerRight corners respectively.
What I want the cRectangle constructor to do, is to instantiate these two points, as it own private points and use them to define the rectangle, do calculations etc. These two points should not be visible to public scope, so a caller can't change them directly.
Here is an example(wrong obviously) for what I try to do:
class cPoint
{
public:
int X,Y;
cPoint();
cPoint(int x, int y)
{
X=x;
Y=y;
}
};
class cRectangle
{
friend class cPoint;
public:
Rectangle(int x1,int y1,int x2,int y2) //the constructor of cRectangle
{
ul(x1,y1);
dr(x2,y2);
}
unsigned int area()
{
return((dr.X-ul.X) * (dr.Y-ul.Y));//example function that uses ul,dr
}
private:
cPoint ul; //uP lEFT point
cPoint dr; //dOWN Right point
};
The error that i get is " Error 1 no match for call to '(cPoint) (int&, int&)' "
Thank you
/* use initilization list */
Rectangle(int x1,int y1,int x2,int y2) : ul(x1,y1), dr(x2,y2)
{
}
Your code won't compile because here
Rectangle(int x1,int y1,int x2,int y2) //the constructor of cRectangle
{
ul(x1,y1);
dr(x2,y2);
}
you wanted to call operator(int,int) on instances of cPoint class and definition of this operator has been not provided.
Once you enter the body of a constructor, all members are initialized and you can only assign to them and everything else that you can do to a living object.
By the point you attempt this
ul(x1,y1);
dr(x2,y2);
ul and dr have already been default initialized (default constructor is the one that's called if you don't explicitly initializer members - read on). When compiler sees these two lines, it looks like you're trying to call overloaded operator() that doesn't exist, so it naturally reports an error.
To initialize members, use constructor initialization list:
Rectangle(int x1,int y1,int x2,int y2)
: ul(x1,y1), dr(x2,y2) // <-- initialization list
// using cPoint::Cpoint(int, int) constructor
{
// note, empty body
}
Related
I have a program with two classes, one for points and one for a rectangle. I need to make two objects of point class within my rectangle class and make them private. I can't figure out how to use the rectangle constructor to make the points with the points constructor, every time I do so I get the error "no default constructor exists for class "Point2d"". Even when I make a default constructor I still get this. I'm at a loss here.
#include <iostream>
#include <cmath>
using namespace std;
class point2D {
private:
int x = 0;
int y = 0;
public:
int getX() {
return x;
}
int getY() {
return y;
}
point2D(int ax, int ay) {
x = ax;
y = ay;
}
};
class rectangleType {
private:
point2D p0;
point2D p1;
public:
int getX0() {
return p0.getX();
}
int getY0() {
return p0.getY();
}
int getX1() {
return p1.getX();
}
int getY1() {
return p1.getY();
}
int getWidth() {
return abs(getX1() - getX0());
}
int getLength() {
return abs(getY1() - getY0());
}
int getPerimeter() {
return 2 * (getWidth() + getLength());
}
int getArea() {
return (getWidth() * getLength());
}
rectangleType(int ax0, int ay0, int ax1, int ay1) {
point2D p0(ax0, ay0);
point2D p1(ax1, ay1);
}
};
Members and base classes not otherwise specifically initialized in the base+member initialization list require provision of default construction. In it's simplest form, given this:
class S
{
public:
S(int) {}
};
this is not possible:
S s;
because the only ctor (default copy-ctor notwithstanding) defined requires an int argument and none was provided. This is, perhaps, intentional by whomever designed S . It may not make sense to have an S without that require argument.
Carrying this forward to your specific circumstance, you have a class, point2D, whose only defined course of construction (default copy-ctor notwithstanding) is this:
point2D(int ax, int ay)
Like S in our previous example, this means something like this:
point2D pt;
is not possible. But that is exactly what is happening here (along with several other problems)
rectangleType(int ax0, int ay0, int ax1, int ay1)
// here, before entering the function body below, p0 and p1
// must be successfully constructed *somehow*
{
point2D p0(ax0, ay0);
point2D p1(ax1, ay1);
}
Before the constructor body is entered, all base classes (of which you have none), and member variables (of which you have two: p0 and p1), must be constructed. Since you are not directly initializing your p0 and p1 members in a member initialization list, the compiler attempts to find default constructors (takes no arguments, or has sufficient default values for all arguments declared). It can't find one. By providing a non-default ctor as you have, you've declared "this is the way these should be constructed". The compiler is therefor saying it could not find a way to create these things successfully.
In your case, providing such a constructor, while possible, isn't necessarily the proper (and certainly not the only) solution. Yes, you can, but it turns out all that does is enhance later issues. For example that will allow this to compile:
rectangleType(int ax0, int ay0, int ax1, int ay1)
{
point2D p0(ax0, ay0);
point2D p1(ax1, ay1);
}
But now you have another problem. Your members p0 and p1 are not the ones you see above. All the code above does is declare two more id's with the same names as your members (thereby shadowing the latter into obscurity), construct them, discard them on function exit, and ultimately leave you with your two members default-initialized, but not with the provided arguments. Shortly thereafter, you scratch your head and wonder went wrong.
You could still provide that default-ctor for point2D (which you didn't want to before, but now feel somewhat 'forced' to), and then do this:
rectangleType(int ax0, int ay0, int ax1, int ay1)
{
p0 = point2D(ax0, ay0);
p1 = point2D(ax1, ay1);
}
But now we're just piling on the rubbish, and an anti-pattern is forming. Now we're default-constructing our p0 and p1 members using a ctor we didn't think we needed (and perhaps even purposely didn't want), and then were discarding those efforts by constructing two more point2D objects and using copy-assignment to reap them.
There is a better way
Member Initialization
The language provides a way of telling telling the compiler, "before entering my constructor body, first construct my base class (if any) and members like this" :
rectangleType(int ax0, int ay0, int ax1, int ay1)
: p0(ax0, ay0)
, p1(ax1, ay1)
{
}
The syntax may seem a bit odd, but apart from the leading colon you've seen the rest before. In fact, you were even using it before in your prior code, but in the wrong place, and for the wrong target variables. This syntax says "construct these members like this, then enter the class constructor function body."
Another Example
That isn't the only place you can (or should) use this feature of the language. For example, your point2D constructor looks like this:
point2D(int ax, int ay)
{
x = ax;
y = ay;
}
But now you know you can also do this:
point2D(int ax, int ay)
: x(ax)
, y(ay)
{
}
Granted, it won't matter much, and any compiler with a grain of sanity will generate similar/identical code in the above trivial usage of this feature, but it stresses a larger overall theme. Where possible, use member-initialization lists to construct members. It is generally more efficient, and in many cases massively so.
A Special Note About Ordering
I mention this only because it is often relevant, ,and can be the cause of some interesting behavior you were otherwise not expecting. When using a member initialization list, the order of construction of the members is not dictated by the order of the list; it is dictated by the order of the member declarations. An example to show what I mean.
In your class, points p0 and p1 are declared in that specific order, top-down. Using our newly discovered member-initialization syntax, what happens when we do this (note the order in the list of members):
rectangleType(int ax0, int ay0, int ax1, int ay1)
: p1(ax1, ay1)
, p0(ax0, ay0)
{
}
This looks like p1 will be constructed before p0. That is not the case. The order of declaration in the class is what matters. Since the class as this:
private:
point2D p0;
point2D p1;
That means p0 will be constructed first, then p1, regardless of the order in the member-initialization list. This can lead to some interesting behavior, especially when you're not expecting it. In your trivial case it wouldn't overall matter, but it is worth noting nonetheless.
Your error tells you that you have to create a default constructor for the points class, e.g.
point2D() {}
or if you want to move the x & y initialization to the constructor
point2D()
: x { 0 }, y { 0 } {}
It happens because you've created a specialized constructor that takes two parameters. If not for that, the compiler would have generated a default constructor for you. :)
As for the initialization in the rectangle class try this:
rectangleType(int ax0, int ay0, int ax1, int ay1)
: p { { ax0, ay0 } }, p1 { { ax1, ay1 } } {}
Also, to remove redundant assignment (thanks #user4581301) you could transform your current 2D(int ax, int ay) into this:
point2D(int ax, int ay)
: x { ax }, y { ay } {}
Further reading:
https://en.cppreference.com/w/cpp/language/constructor (again thank you #user4581301)
your constructor should look like this:
rectangleType(int ax0, int ay0, int ax1, int ay1)
: p0 {ax0, ay0},
p1 {ax1, ay1}
{}
this is a member initialization list, you initialize values p0 and p1 by passing the arguments to their respective construcotrs
I have a 2d physics engine that I've been programming in C++ using SFML; I've implemented a rough collision detection system for all SandboxObjects (the base class for every type of physics object), but I have a dilemma.
I plan to have many different derived classes of SandboxObjects, such as Circles, Rects, and so on, but I want a way to check if the roughHitbox of each SandboxObject collides with another.
When the program starts, it allocates memory for, let's say, 10,000 Circles
int circleCount = 0;//the number of active Circles
constexpr int m_maxNumberOfCircles = 10000;//the greatest number of circles able to be set active
Circle* m_circles = new Circle[m_maxNumberOfCircles];//create an array of circles that aren't active by default
like so.
and every time the user 'spawns' a new Circle, the code runs
(m_circles + circleCount)->setActive();`
circleCount++
Circles that aren't alive essentially do not exist at all; they might have positions and radii, but that info will never be used if that Circle is not active.
Given all this, what I want to do is to loop over all the different arrays of derived classes of SandboxObject because SandboxObject is the base class which implements the rough hitbox stuff, but because there will be many different derived classes, I don't know the best way to go about it.
One approach I did try (with little success) was to have a pointer to a SandboxObject
SandboxObject* m_primaryObjectPointer = nullptr;
this pointer would be null unless there were > 1 SandboxObjects active; with it, I tried using increment and decrement functions that checked if it could point to the next SandboxObject, but I couldn't get that to work properly because a base class pointer to a derived class acts funky. :/
I'm not looking for exact code implementations, just a proven method for working with the base class of many different derived classes.
Let me know if there's anything I should edit in this question or if there's any more info I could provide.
Your problems are caused by your desire to use a polymorphic approach on non-polymorphic containers.
The advantage of a SandboxObject* m_primaryObjectPointer is that it allows you to treat your objects polymorphicaly: m_primaryObjectPointer -> roughtHitBox() will work regardless of the object's real type being Circle, Rectangle, or a Decagon.
But iterating using m_primaryObjectPointer++ will not work as you expect: this iteration assumes that you iterate over contiguous objects in an array of SandboxObject elements (i.e. the compiler will use the base type's memory layout to compute the next address).
Instead, you may consider iterating over a vector (or an array if you really want to deal with extra memory management hassle) of pointers.
vector<SandboxObject*> universe;
populate(universe);
for (auto object:unviverse) {
if (object->isActive()) {
auto hb = object -> roughtHitBox();
// do something with that hitbox
}
}
Now managing the objects in the universe can be painful as well. You may therefore consider using smart pointers instead:
vector<shared_ptr<SandboxObject>> universe;
(little demo)
It's hard to answer this without knowing the requirements but you could have sandbox maintain two vectors of active and inactive objects, and use unique_ptrs of the base class for memory management.
Some code below:
#include <vector>
#include <memory>
#include <iostream>
class sandbox_object {
public:
virtual void do_something() = 0;
};
class circle : public sandbox_object {
private:
float x_, y_, radius_;
public:
circle(float x, float y, float r) :
x_(x), y_(y), radius_(r)
{}
void do_something() override {
std::cout << "i'm a circle.\n";
}
};
class triangle : public sandbox_object {
private:
float x1_, y1_, x2_, y2_, x3_, y3_;
public:
triangle( float x1, float y1, float x2, float y2, float x3, float y3) :
x1_(x1), y1_(y1), x2_(x2), y2_(y2), x3_(x3), y3_(y3)
{}
void do_something() override {
std::cout << "i'm a triangle.\n";
}
};
class sandbox {
using sandbox_iterator = std::vector<std::unique_ptr<sandbox_object>>::iterator;
private:
std::vector<std::unique_ptr<sandbox_object>> active_objects_;
std::vector<std::unique_ptr<sandbox_object>> inactive_objects_;
public:
void insert_circle(float x, float y, float r) {
active_objects_.push_back( std::make_unique<circle>(x, y, r) );
}
void insert_triangle(float x1, float y1, float x2, float y2, float x3, float y3) {
active_objects_.push_back( std::make_unique<triangle>(x1,y1,x2,y2,x3,y3));
}
sandbox_iterator active_objs_begin() {
return active_objects_.begin();
}
sandbox_iterator active_objs_end() {
return active_objects_.end();
}
void make_inactive(sandbox_iterator iter) {
std::unique_ptr<sandbox_object> obj = std::move(*iter);
active_objects_.erase(iter);
inactive_objects_.push_back(std::move(obj));
}
};
int main() {
sandbox sb;
sb.insert_circle(10.0f, 10.0f, 2.0f);
sb.insert_triangle(1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 1.0f);
sb.insert_circle(1.0f, 6.0f, 4.0f);
sb.make_inactive(sb.active_objs_begin());
(*sb.active_objs_begin())->do_something(); // this should be the triangle...
return 0;
}
I created a class for a type of enemy using allegro4 and C++; in this class I have a function that makes move a sprite, like this:
sprite_one(x, y);
sprite_two(x2, y2);
class enemy{
public:
void mov(){
x++;
----
y--;
}
}
};
enemy test_one;
test_one.mov(); // this works because its coordinates are x and y
enemy test_two;
test_two.mov(); // this doesn't work, its coordinates are x2 and y2
The problem is that when I create the object, the first one can move according to the function (updating variable x and y), the others no because they have different way to call the variables of the positions. How can I fix this?
Your enemy class needs to have the x and y coordinates as member variables. This is how you get each actual enemy to have its own coordinates separate from all the others. The following code should get you up and running, at least. You will presumably want to add a public function to print the coordinates, or to draw the enemy onscreen.
class enemy
{
int mx, my; // coordinates of this enemy
public:
enemy(int x, int y)
: mx(x), my(y) // initialize the coordinates
{
// possibly add future initialization here
}
void mov()
{
++mx;
--my;
}
}
Then you can create and move two enemies as before:
enemy test_one(x,y);
test_one.mov();
enemy test_two(x2,y2);
test_two.mov();
Note that x,y,x2,y2 are no longer variables storing the current positions of the enemies, but constants defining their start positions.
The following code throws the aforementioned error message at the line where an object is added to a vector:
include "palm.h"
include <vector>
using namespace std;
class forehead {
public:
void palmstrikesforehead(float x, float y, float z);
private:
vector<palm> palms;
};
void forehead::palmstrikesforehead(float x, float y, float z) {
Palm::Palm palm(x,y,z);
palms.push_back(palm); //Exception thrown if this line is not commented out.
}
The code works with a vector of integer type, but not with a vector of Palm type. What I would like to do is to anonymously add Palms to the vector (as in the java: arrayList.add(new Palm(x,y,z));. How may that be done?
The class name seems to be palm. So you should write
palm the_palm (x, y, z);
//^^^^
palms.push_back(the_palm);
Or you could construct the palm in-place:
palms.push_back(palm(x, y, z));
If Palm is the correct class name (you don't get a compile error), then Palm::Palm palm(x,y,z); should be Palm palm(x,y,z);
class forehead {
public:
void palmstrikesforehead(float x, float y, float z);
private:
vector<Palm::Palm> palms;
};
This is not possible to solve at present as we don't know what is in Palm never mind what it's called.
Assuming that Palm is your own class, there is potentially an issue with your copy-constructor. Is your class obeying the "rule of 3"?
To "anonymously add", assuming your class is called palm, you would do:
palms.push_back( palm(x,y,z) );
hi im trying to pass some values to a class but it wont let me it says invalid use of class 'Figure' im trying to send 3 values x,y,z and thats all but it wont let me heres what im trying to do...
here is the main.cpp and the function that calls the class Figure
for (j = 0; j < num_elems; j++) {
/* grab and element from the file */
vlist[j] = (Vertex *) malloc (sizeof (Vertex));
ply_get_element (ply, (void *) vlist[j]);
int vert=sprintf(szFile,"vertex: %g %g %g", vlist[j]->x, vlist[j]->y, vlist[j]->z);
/* print out vertex x,y,z for debugging */
TextOut(hDC,600,j*20,szFile,vert);
DrawFig->Figure(vlist[j]->x, vlist[j]->y, vlist[j]->z);
}
The error is here
DrawFig->Figure(vlist[j]->x, vlist[j]->y, vlist[j]->z);
}
Here is the WM_CREATE: where i initialize everything
case WM_CREATE:
hDC = GetDC(hWnd);
//ShowWindow(g_hwndDlg,SW_SHOW);
hRC=wglCreateContext(hDC);
wglMakeCurrent(hDC,hRC);
g_hwndDlg = CreateDialog(hInst,MAKEINTRESOURCE(IDD_DIALOG1),hWnd,DialogProc);
DrawFig= new Figure(1.0,1.0,1.0);
initGL();
break;
here is the Figure.h
class Figure
{
public:
Figure(float x,float y,float z);
void Draw();
float paramx(){
return x1;
}
float paramy(){
return y1;
}
float paramz(){
return z1;
}
protected:
private:
float x1,y1,z1;
list <Figure> m_vertices;
};
and here is the Figure.cpp
Figure::Figure(float x,float y,float z){
this->x1=x;
this->y1=y;
this->z1=z;
m_vertices.push_back(Figure(x1, y1, z1));
}
void Figure::Draw()
{
list<Figure>::iterator p = m_vertices.begin();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0.0,0.0,4.0,0.0,0.0,0.0,0.0,1.0,0.0);
glColor3f(0.7f,1.0f,0.3f);
glBegin(GL_LINE_LOOP);
while(p != m_vertices.end()){
glNormal3f(p->paramx(),p->paramy(),p->paramz());
glVertex3f(p->paramx(),p->paramy(),p->paramz());
p++;
}
glEnd();
}
any ideas? this is opengl,c++ and im using codeblocks 10.05 just in case
oh yeah im initializing it at the main.h like this DrawFig* Figure;
#dark_charlie's answer is almost correct. Here is a better version that will actually work, but still probably isn't what you want:
class Figure {
// ...
public:
void set(float x, float y, float z);
// ...
};
void Figure::set(float x, float y, float z)
{
// Your original code from the constructor
this->x1 = x;
this->y1 = y;
this->z1 = z;
}
Figure::Figure(float x, float y, float z)
{
// In the constructor call the newly created set function
set(x, y, z);
m_vertices.push_back(Figure(x1, y1, z1));
}
// Replace the faulty line with this:
DrawFig->set(vlist[j]->x, vlist[j]->y, vlist[j]->z);
Now, this is almost certainly not what you want. But it's also really hard to figure out what you do want. You have a design problem. The design problem is that Figure has two responsibilities. It is both a point in space, and a set of points describing a figure. This confusion of responsibilities is leading your class to not actually be able to fill either of them particularly well.
You need two classes. You need a Point class and a Figure class. The Figure class should allow you to set the location of the figure as well as letting you add points to the figure's outline.
The huge clue that something is wrong is this list<Figure> m_vertices;. It's very rare that a class conceptually contains instances of itself. And usually when you do it you're building your own data structure like a tree or a list and then the class contains pointers to instances of itself.
Also, the fact that #dark_charlie's simple fix resulted in infinite recursion is another huge clue that something is wrong.
I'm guessing this is a homework assignment, so this is all the help I will give you aside from telling you that I think you already have a Point class that you call Vertex.
Just about the direct constructor call:
Use this instead:
// destruct and reconstruct
DrawFig -> ~Figure();
new (DrawFig) Figure(vlist[j]->x, vlist[j]->y, vlist[j]->z);
What it does:
It calls the destructor.
The destructor itself will call the destructor of all member variables. floats don't need/have a destructor but std::list has. std::lists destructor will free all containing objects.
It calls the constructor.
The constructor itself will call the constructor of all member variables. Again, floats don't have that and they are not initialized in a specific way, i.e. they are ignored again. Then the constructor of std::list is called which will initialize the list.
However, using dark_charlie's solution might be more clean.
Not only is DCs solution more clean, it also does something different. By calling the constructor again, you would also reset Figure::m_vertices and I think this is probably not what you want here.
However, maybe instead of set (like in DCs solution) you should name it add or so instead.
Also I am not sure if you really want to have Figure or Figure::m_vertices that way (each Figure containing a list to other Figures).
You cannot call a constructor directly in the way you attempt to. Create a set() function that will do the same work and use it instead of the constructor:
class Figure {
// ...
public:
void set(float x, float y, float z);
// ...
};
void Figure::set(float x, float y, float z)
{
// Your original code from the constructor
this->x1 = x;
this->y1 = y;
this->z1 = z;
// m_vertices.push_back(Figure(x1, y1, z1));
}
Figure::Figure(float x, float y, float z)
{
// In the constructor call the newly created set function
set(x, y, z);
}
// Replace the faulty line with this:
DrawFig->set(vlist[j]->x, vlist[j]->y, vlist[j]->z);
EDIT:
As noted in the comments, the code has yet another flaw - you have a list of figures that is contained within the Figure itself. I think you meant to declare m_vertices as follows:
list <Vertex> m_vertices;
Then, however, if you want a Figure to be a triangle (or any other higher-order polygon), you will need to pass coordinates of all three vertices instead of the three coordinates of one vertex:
void Figure::set(const Vertex& v1, const Vertex& v2, const Vertex& v3)
{
m_vertices.push_back(v1);
m_vertices.push_back(v2);
m_vertices.push_back(v3);
// The position of the figure will be its centroid
this->x1 = (v1.x + v2.x + v3.x) / 3;
this->y1 = (v1.y + v2.y + v3.y) / 3;
this->z1 = (v1.z + v2.z + v3.z) / 3;
}
Figure::Figure(const Vertex& v1, const Vertex& v2, const Vertex& v3)
{
set(v1, v2, v3);
}
You will also need to adjust the loop to read 3 vertices at once instead of only one but I'll let that up to you :)
A few things:
Did you instantiate the Figure class?
Is the list <Figure> m_vertices; instantiated?
The usage of using C's malloc function with the C++ runtime code is messy, best to stick with new instead to keep the C++ runtime consistent.