Consider the following example:
class Rectangle{
Rectangle(int x, int y, int width, int height);
Rectangle(int topLeft_x, int topLeft_y, int bottomRight_x, int bottomRight_y);
};
A Rectangle object may be built giving (x,y) coordinates plus width and height or giving top left points pair and bottom right points pair.
While this is correct from an object oriented point of view, this is not from a compiler point of view, returning the error "member function already defined or declared"
While I normally fix this condition easily in case of member function, simply changing the name according to what it does, this is not possible for constructors.
What is the simpler and correct way to workaround this issue keeping both the way to construct the object?
Another possible solution (other than the pair suggested by #VladfromMoscow) is a static method to perform construction. This lets you give them distinct names, since their argument lists are so similar. Thisi is called the Named Constructor Idiom
class Rectangle
{
public:
static Rectangle createRectangle(int x, int y, int width, int height)
{
return Rectangle(x,y,width,height);
}
static Rectangle createRectangleCorners(int x1, int y1, int x2, int y2)
{
return Rectangle(x1,y1,x2-x1, y2-y1);
}
private:
// Doesn't have to be private, but this forces users to use the above
// constructors
Rectangle(int x, int y, int width, int height);
}
You wrote already yourself
top left points pair and bottom right points pair
So what you need is to define class Point and use this type in the constructor declaration.
Otherwise the constructors are declared like
class Rectangle{
Rectangle(int, int, int, int);
Rectangle(int, int, int, int);
};
As you see these declarations do not make sense even if you will write multiline comments.:)
Another approach is to declare the first constructor like
class Rectangle{
Rectangle(int x, int y, unsigned int width, unsigned int height);
Rectangle(int topLeft_x, int topLeft_y, int bottomRight_x, int bottomRight_y);
};
However this approach is unsafe because each integer literal specified as the third or fourth argument must be casted.
Instead of the class Point you could use standard class std::pair. For example
#include <utility>
//...
class Rectangle{
Rectangle(int x, int y, int width, int height);
Rectangle( const std::pair<int, int> &topLeft, const std::pair<int, int> &bottomRight);
};
Another way on how to solve this problem is by using Tag dispatching:
Instead of using methods with different names, give them a new parameter, e.g.,
struct useCorners {};
struct useDimension {};
class Rectangle
{
Rectangle(useCorners, int topLeft, int topRight, int bottomLeft, int bottomRight)
{ ...
}
Rectangle(useDimension, int topLeft, int topRight, int width, int height)
{ ...
}
};
Related
I'm trying to make a program that deals with images, in which I have an image object and an image_view object that references a rectangle region in the image:
class image_view;
class image
{
public:
image(int width, int height);
operator image_view() const;
private:
int m_width;
int m_height;
std::vector<pixel> m_pixels;
};
class image_view
{
public:
image_view(const image& ref, point origin, int width, int height);
image_view(image_view view, point origin, int width, int height);
private:
const image& m_ref;
point m_origin;
int m_width;
int m_height;
};
However, when I tried to copy an image_view, the compiler told me that the operator= member function had been deleted because of the non-static member reference. I naively tried to make my own member function with m_ref = other.m_ref but it didn't work since m_ref is const.
I considered using a smart pointer instead of a reference, but I didn't find a way of making a smart pointer to an already existing object.
One solution I found is the following:
image_view& image_view::operator= (const image_view& other)
{
*this = image_view(other);
return *this;
}
This compiles, but is it a good idea? (I might be wrong, but I feel bad about assigning stuff to *this.) If I do that, does that mean I have to define a destructor (even though I'm not allocating anything), move constructor and move assignment operator to satisfy the rule of 5?
If you want to express a non-owning, non-null wrapper that rebinds on assignment, the easiest way to do that is to use std::reference_wrapper:
class image_view
{
public:
image_view(const image& ref, point origin, int width, int height);
image_view(image_view view, point origin, int width, int height);
private:
std::reference_wrapper<image const> m_ref;
point m_origin;
int m_width;
int m_height;
};
The default copy constructor and assignment operator will do the right thing.
I've been looking all around StackOverlow for an answer but i didn't find one so i hope it isn't duplication of any post around here.
so, i have the next problem.
lets say i have the next 2 classes: Rectangle (Which is built from another class but it doesn't concern us currently)
and Grid.
with they following constructors:
(Point Constructor for Rectangle private topLeft and bottomRight):
Point::Point(int x, int y) {this->x = x; this->y = y;}
(Rectangle Constructor and Class)
class Rectangle
{
public:
Rectangle(int l, int u, int w, int h, int color);
//int getColor() const;
//void setColor(int color);
//bool contains(const Point &p) const;
//void print() const;
private:
const Point topLeft, bottomRight;
int color;
};
Rectangle::Rectangle(int l, int u, int w, int h, int color) :
topLeft(l, u),
bottomRight(l + w, u + h)
{ this->color = color; }
(Grid Constructor and Class) (Lets assume I don't want to initialize the values of Rectangle in Grid just allocate them in memory)
class Grid
{
public:
Grid(int tileW, int tileH, int width, int height, int color);
//~Grid();
//Rectangle& getRectAt(const Point &p);
//void print() const;
private:
int count;
Rectangle **recs;
};
Grid::Grid(int tileW, int tileH, int width, int height, int color)
{
int index, index_c=0;
recs = new Rectangle *[width];
for (int index = 0; index < width; index++)
{
recs[index] = new Rectangle[index];
}
}
so, as you can understand i have problem in Grid constructor with the following Error
(Error 1 error C2512: 'Rectangle' : no appropriate default constructor available.)
but i just cant understand why it wont work, I've been suggested to allocate the Recs double pointer as 1 dimensional array (Array with the length of Width*Height) but what if Recs was 4 dimensional array ? How could you flat it properly and then index around the 4-dimensional array without having a headache calculating the index of each cell in the array.
another thing, we know that if it was int** and not recs** it would work perfectly
int **foo;
int height,width;
foo = new int* (height);
for (int index = 0; index<height; ++index)
foo[index] = new int[width];
so i just keep missing the way of doing n-dimensional arrays in C++.
The line recs[index] = new Rectangle[index]; tries to call Rectangle's default constructor index times. If you want to create multiple objects at once, you probably want to add default ctor and simple setter method to your Rectangle class
class Rectangle
{
public:
Rectangle(int l, int u, int w, int h, int color);
Rectangle() = default;
void set(int l, int u, int w, int h, int color);
private:
const Point topLeft, bottomRight;
int color;
};
Then, in creation loop:
for (int index_w = 0; index_w < width; index_w++)
{
recs[index_w] = new Rectangle[height]; //see note below
for (int index_h = 0; index_h < height; index_h++)
recs[index_w][index_h].set(/* some math with width, height and loop variables*/, color);
}
Note: I have changed index to height, because you want to create 2D array, so the total grid size is height * width. With index length in creation, you would create triangle-shaped grid instead (and more over, the first loop iteration would be recs[0] = new Rectangle[0] - a zero-length array).
As user Macro A mentioned, consider using std::vector<Rectangle> instead of raw pointers (2D array would be std::vector<std::vector<Rectangle>>)
Also, consider changing your design, because currently you are creating a grid H x W of Rectangle objects, where all points (except first/last) are duplicated across adjacent rectangles (each point is upper-left corner of one rectangle, upper-right corner of another, bottom-left...).
I propose a Grid class that holds 2D array of ints and has a method Rectangle getRectangle(int x, int y) which would return appropriate set of 2 points. Modyfying such Grid class would be much easier, and you wouldn't have to iterate over all Rectangles, just ints
You could use a placement new : you initially reserve enough place to store the array of objects, then individually construct them each in its own place.
In your code, it could become (more or less):
for (int index = 0; index < width; index++)
{
// first simple allocation for the array
recs[index] = (Rectangle *) malloc(sizeof(Rectangle) * height);
for (int j=0; j<height; j++) {
// individually build each rectangle in place
new(&recs[index][j]) Rectangle(index*tileW, j*tileH, tileW, tileH, color);
}
}
This is intended to do exactly what you need : build arrays of non default constructible objects.
Unrelated: as you use raw pointers and allocated arrays, do not forget to correctly free everything. Using std::vectors could save you from that...
You get error C2512 because you don't have a default constructor in Rectangle. The only constructor you have in Rectangle is parameterized, while a default constructor has the requirement that it must be callable without additional arguments provided.
Looking at the line recs[index] = new Rectangle[index];, you see there are no arguments for the 5 parameters the constructor accepts. For that line to compile, you need to create a default constructor either with a new constructor with the signature Rectangle() or default arguments for the parameters in your parameterized constructor, for example Rectangle(int l = 0, int u = 0, int w = 0, int h = 0, int color = 0);.
If you want to allocate the memory, you should create the default constructor and use std::vector<Rectangle> which you initialize with a size. Then you can later replace the objects by use of a copy-constructor, as shown in this example: http://ideone.com/KnUBPQ
As for creating an n-dimensional array, you've explicitly limited it to a two-dimensional array with your Rectangle and Point classes. If the array is going to be n-dimensional, your points need to be n-dimensional too:
template<int dimensions>
class Point
{
std::array<int, dimensions> coords;
...
};
or
class Point
{
std::vector<int> coords;
...
};
for some reason I can´t achieve this.
Line::Line(const Pixel &aStart, const Pixel &aEnd){
start = aStart;
end = aEnd;
}
the Line class:
class Line : public Vertex{
public:
Line(const Pixel &start, const Pixel &end);
Pixel getStart();
Pixel getEnd();
private:
Pixel start;
Pixel end;
};
g++ tells me
error: no matching function for call to ‘Pixel::Pixel()’
note: candidates are:
- Pixel::Pixel(int, int, int, int, int)
- Pixel::Pixel(int, int)
- Pixel::Pixel(const Pixel&)//not implemented by me, some sort of default constructor?
I thought actually Im using the last constructor, but something doesnt work. Any help much appreciated.
EDIT: The Pixel Class:
class Pixel{
public:
Pixel(int x, int y);
Pixel(int red, int green, int blue, int x, int y);
void setRed(int red);
void setGreen(int green);
void setBlue(int blue);
void setColor(int r, int g, int b);
int getRed();
int getGreen();
int getBlue();
bool isChanged();
int getX();
int getY();
private:
int red;
int green;
int blue;
int x;
int y;
bool hasBeenChanged;
};
The Line members of type Pixel, start and end, will be default constructed before the constructor of Line runs and sets them to the objects passed in as arguments. It seems that your Pixel class doesn't have a default constructor (because giving any user-defined constructor will stop the compiler from giving you an implicit default constructor). You need to make use of a constructor member initialization list:
Line::Line(const Pixel &aStart, const Pixel &aEnd)
: start(aStart), end(aEnd)
{ }
This initialises start and end with aStart and aEnd respectively, bypassing the default construction that would normally occur.
Because you declare this
Pixel::Pixel(int, int, int, int, int)
Pixel::Pixel(int, int)
the compiler doesn't generate a default constrcutor, or a constructor exists with arguements having default values.
Pixel start;
Pixel end;
These require a default constructor. It doesn't find one, hence the compiler complains. You current code requires the default constructors.
And as a good practice use the initialization list, which wouldn't ask the arguements to default constructed.
Line::Line(const Pixel &aStart, const Pixel &aEnd):start(aStart), end(aEnd){}
The constructor should, to my knowledge, be defined in the implementation file but I've only been able to find examples with the class inside one main file instead of split into a .h and .cpp file
All I need to know is if my following code is separated in an acceptable manner..
Entity.h:
using namespace std;
class cEntity {
private:
/*-----------------------------
----------Init Methods---------
-----------------------------*/
int *X, *Y;
int *Height, *Width;
public:
/*-----------------------------
----------Constructor----------
-----------------------------*/
cEntity (int,int, int, int);
/*-----------------------------
----------Destructor-----------
-----------------------------*/
~cEntity ();
/*-----------------------------
----------Set Methods----------
-----------------------------*/
/*Set X,Y Methods*/
void setX(int x){*X=x;};
void setY(int y){*Y=y;};
void setXY(int x, int y){*X=x; *Y=y;};
/*Set Height, Width Methods*/
void setHeight(int x){*Height=x;};
void setWidth(int x){*Width=x;};
void setDimensions(int x, int y){*Height=x; *Width=y;};
/*-----------------------------
----------Get Methods----------
-----------------------------*/
/*Get X,Y Methods*/
int getX(){return *X;};
int getY(){return *Y;};
/*Get Height, Width Methods*/
int getHeight(){return *Height;};
int getWidth(){return *Width;};
};
and Entity.cpp:
#include "Entity.h"
cEntity::cEntity (int x, int y, int height, int width) {
X,Y,Height,Width = new int;
*X = x;
*Y = y;
*Height = height;
*Width = width;
}
cEntity::~cEntity () {
delete X, Y, Height, Width;
}
I would also like to say thanks to everyone for being so helpful, especially on my first question!
cEntity::cEntity (int x, int y, int height, int width) {
is correct
X,Y,Height,Width = new int;
not so much. That sets Width to a new int, but not the rest. You probably intended:
X = new int(x);
Y = new int(y);
Height = new int(height);
Width = new int(width);
Note that this method of construction will not work for objects without assignment/copy, like references. For some objects, it's also slower than constructing them in place. As such, the preferred way to construct is like so:
cEntity::cEntity (int x, int y, int height, int width) {
:X(new int(x))
,Y(new int(y))
,Height(new int(height))
,Width(new int(width))
{}
This is better, but if any exceptions are thrown, you'll have to somehow deallocate the ones that were allocated. Better is to make each of those members a std::unique_ptr<int>, so they'll deallocate themselves and save you many headaches.
Yes, it's OK.
However, there is a problem with your constructor and destructor.
What your code actually does is allocating one int and your destructor deallocates one int also.
Anyway, there is no need to use pointers here.
Somewhat better implementation (if we don't use smart pointers), could be:
[Entity.h]
private:
/*Private fields*/
int X, Y;
int Height, Width;
[Entity.cpp]
cEntity::cEntity (int x, int y, int height, int width) {
X = x;
Y = y;
Height = height;
Width = width;
}
cEntity::~cEntity () {
}
And one more thing. Try to avoid using namespace std; in your header files. If you do, you force those who include your header to use this using statement and it can provoke namespace clashes.
Your separation is fine. The implementations of those functions is wrong, but you've separated them from the declaration suitably. (They don't allocate or free as many objects as you think they do.)
Yes. For the separation at least, that's generally the best way to do it.
As for the actual implementation you have some issues. I am not really sure what you are trying to do with the constructor or if you have the correct data types for the class member variables but something seems off.
Any method defined in the class directly is implicitly inlined, including the constructor.
I.e.
class MyClass
{
public:
MyClass() {};
};
defines an inline constructor, which may (or may not) improve your code performance,
Whereas
class MyClass
{
public:
MyClass();
};
MyClass::MyClass()
{
};
is not inlined, and therefore won't have those benefits. Both options are correct C++ though.
Just my 2 cents.
P.S And yes, when you decide to store pointers inside a class in this manner, you open a Pandora box.
I have a couple of cases of circular declaration in my class delaractions in my main (global) header.
#include <cstdlib>
#include <iostream>
using namespace std;
enum piece_t {BLACK, WHITE, EMPTY, WALL}; //wall is area out side of board (board array is 21x21 but only 19x19 is playable)
enum dir_t {ABOVE,BELOW,LEFT, RIGHT}; //shall i overload ! or - operatior? !LEFT==RIGHT?
struct nextPoint_t //should be implimented with references, but need to practice pointer
{
point_t* above;
point_t* below;
point_t* left;
point_t* right;
};
class point_t
{
private:
piece_t mType; //what sort of point this is
int mLiberties;
nextPoint_t mAdjacent; // points to adjacent points
bool mLibertiesCounted; // keeps track of if liberties have been counted, for mCountLiberites() (sets), is reset by mUpdateLiberites();
int mCountLiberties(); //counts this point's liberites, by calling count on mAdjacent points etc.
void mSetPos(int xPos, int yPos, board_t theBoard); //sets up mAdjacent to point to adjacent points,
void mSetStructureLiberties(int numLibs); // Sets this squares liberites then calls this on all adjacent squares
public:
point_t ();// parameterless constructor, for arrays
void mSetUp(int xPos, int yPos, board_t theBoard);// sets up mType then calles setPos iFF not WALL type
point_t (int xPos, int yPos, board_t theBoard); //constructor, takes it's position in the grid as a parameter
void mUpdateLiberties(); // calles countLiberties then, updates liberites on whole of connected structure, by operating pon all conencted points
};
class board_t
{
private:
point_t mArray [21][21];
public:
board_t(); //constructor, sets up board by operating on the point_t's
};
Don't worry about the comments there for my reading, I know what I mean.
I thought I could fix it with forward declarations, but they don't seem to work, it just thinks I'm redeifining the classes
Alright, after considering the comments and doing a test on my own, the real answer :
You must use forward declaration, no more. :)
#include <cstdlib>
#include <iostream>
class point_t;
class board_t;
/* Rest of the code stay the same */
The forward references in your code seems to be to board_t and point_t, which is resolved by forward declaring them.
Since you reference board_t in member function declarations of point_t, you cannot define the member functions right away in point_t. Their definitions have to appear after board_t is defined. So you have to either move the definition of the functions into the cpp file, or you have to move their definition in the header after definition of board_t whichever suites you more. point_t is only used as a pointee type in nextPoint_t, so we don't have the same problem for it here:
class point_t; // used by nextPoint_t
class board_t; // used by point_t
struct nextPoint_t //should be implimented with references, but need to practice pointer
{
point_t* above; // goes without problems - doesn't need definition of point_t
point_t* below;
point_t* left;
point_t* right;
};
class point_t
{
private:
piece_t mType;
int mLiberties;
nextPoint_t mAdjacent;
bool mLibertiesCounted;
int mCountLiberties();
void mSetPos(int xPos, int yPos, board_t theBoard);
void mSetStructureLiberties(int numLibs);
public:
point_t ();
void mSetUp(int xPos, int yPos, board_t theBoard);
point_t (int xPos, int yPos, board_t theBoard);
void mUpdateLiberties();
};
class board_t
{
private:
point_t mArray [21][21];
public:
board_t();
};
Definition at the end of the header looks like
// define it either inline in the header, or non-inline in a cpp file
inline void point_t::mSetPos(int xPos, int yPos, board_t theBoard) {
/* some work... */
}
// same for mSetUp...
Nonetheless i would recommend you to use const references to pass the board to point_t's member functions, but this is not a requirement for your code to work. Declarations go fine with incomplete parameter types.
if you write before your struct
class point_t;
that should do the trick.
Although I am not quite sure why your organize your class like that. You already have an array mArray in your board so there is no real need to have pointers to adjacent points inside each point_t.
EDIT: as the other poster said before you need to use pointers.
Just add this above struct nextPoint_t
enum piece_t {BLACK, WHITE, EMPTY, WALL};
enum dir_t {ABOVE,BELOW,LEFT, RIGHT};
class point_t;
class board_t;
struct nextPoint_t
{
point_t* above;
point_t* below;
point_t* left;
point_t* right;
};
And change any reference to a board_t to a board_t*
void mSetUp(int xPos, int yPos, board_t* theBoard);
#include <cstdlib>
#include <iostream>
enum piece_t {BLACK, WHITE, EMPTY, WALL}; //wall is area out side of board (board array is 21x21 but only 19x19 is playable)
enum dir_t {ABOVE,BELOW,LEFT, RIGHT}; //shall i overload ! or - operatior? !LEFT==RIGHT?
class point_t;
struct nextPoint_t //should be implimented with references, but need to practice pointer
{
point_t* above;
point_t* below;
point_t* left;
point_t* right;
};
class board_t;
class point_t
{
private:
piece_t mType; //what sort of point this is
int mLiberties;
nextPoint_t mAdjacent; // points to adjacent points
bool mLibertiesCounted; // keeps track of if liberties have been counted, for mCountLiberites() (sets), is reset by mUpdateLiberites();
int mCountLiberties(); //counts this point's liberites, by calling count on mAdjacent points etc.
void mSetPos(int xPos, int yPos, const board_&t theBoard); //sets up mAdjacent to point to adjacent points,
void mSetStructureLiberties(int numLibs); // Sets this squares liberites then calls this on all adjacent squares
public:
point_t ();// parameterless constructor, for arrays
void mSetUp(int xPos, int yPos, const board_t& theBoard);// sets up mType then calles setPos iFF not WALL type
point_t (int xPos, int yPos, const board_t& theBoard); //constructor, takes it's position in the grid as a parameter
void mUpdateLiberties(); // calles countLiberties then, updates liberites on whole of connected structure, by operating pon all conencted points
};
class board_t
{
private:
point_t mArray [21][21];
public:
board_t(); //constructor, sets up board by operating on the point_t's
};