This is the code I'm trying to run:
class poly {
public:
int vnum;
vrtx vrts[this->vnum];
};
(Note: The class name "poly" and other class "vrtx" are named as such to approximate the purpose of the problematic snippet. Vrtx is a class with int x, y, z;)
At first, the code didn't contain the "this->" pointer at all. I was confused why it wasn't working, and then realized that "vnum" doesn't mean anything. I needed an object.poly.vnum sort of thing so that I'm referencing a specific value. I tried "this.," "this.poly.," and the displayed "this->," but none of them work. I'm not great with pointers, so any advice would be appreciated!
I've looked at similar questions, but none of them address this issue in such a way that I could make the necessary fix with the information provided.
Here's a code fragment that should help.
class Poly
{
public:
int vnum;
std::vector<vrtx> vrts;
Poly(int capacity)
: vnum(capacity)
{ vrts.resize(vnum);}
};
The above fragment uses std::vector since the std::vector can expand dynamically (at run-time). The constructor uses the resize method to expand the std::vector to the given capacity.
Arrays are a pain to resize during run-time, so use std::vector.
I have two classes called Renderable and Triangle. The latter is supposed to be deriving from the former. I plan to be passing an array and its length into the Triangle constructor, which in turn will pass it to the constructor in the Renderable class that will handle the corresponding parameters. Here are the header files:
Renderable.h
class Renderable {
protected:
const int dataLength = 0;
const float* data[0] ;
public:
Renderable();
Renderable(int dataLength, float* data[]);
virtual void render() const =0 ;
};
Triangle.h
#pragma once
#include <glad/glad.h>
#include "Renderable.h"
class Triangle : public Renderable {
public:
void render() const;
};
So the problem is that the error check gave me invalid base class. I did some research and saw something about arrays' not being able to be an instance variable without specified size but this is still quite confusing to me. Could someone please enlighten me on this?
Also is there a more efficient solution to problems like this?
Thank you!
const float* data[0] ;
This array member declaration is ill-formed. The size of an array variable may not be 0.
Otherwise the example is well-formed (assuming the functions are defined in some translation unit).
Also is there a more efficient solution to problems like this?
Solution is to not declare an array variable of size 0.
So the problem here is that the code design that I was going for is undesirable and I should either use std::vector or making another subtype of the Renderable class with a fixed array length and then extend from it.
I am trying to have a declaration of 2d array in private part of my class and then latter on, the functions of this class will use and modify the values in the array, but I am getting an error which says "I-value specified const object". What is wrong with this code and how can I fix it?
Here is a simple code demonstrating my problem
in someClass.h________________
class someClass
{
public:
//Some code here
private:
char grid[20][20];
//Some code here
}
in someClass.cpp______________
lets say one of the functions is trying to reassign the value of item in position (0,0) like so.
grid[0][0]='*';
This gives me an error saying, expression must be modifiable lvalue.
Move it from private to public so you can access it outside the class. If it is private then only functions inside the class can use it.
This question already has answers here:
What is object slicing?
(18 answers)
Closed 8 years ago.
I'm trying to create a tile system which creates a single instance of a tile, and adds the tile to an array of tiles, allowing you to call Tile::s_tileList[1] to get the dirt tile.
However, I'm having some issues with it. Here's the complete tile code:
tile.h
#pragma once
class Tile {
public:
static const Tile s_tileList[128]
static const Tile s_dirt;
Tile(int tileId);
virtual ~Tile();
protected:
private:
int m_tileId;
};
tile.cpp
#include "tile.h"
#include "tile_dirt.h"
const Tile Tile::s_dirt = TileDirt(1); // This line errors
Tile::Tile(int tileId) {
s_tileList[tileId] = *this;
m_tileId = tileId;
}
Tile::~Tile() {
}
tile_dirt.h
#pragma once
#include "tile.h"
class TileDirt : Tile {
public:
TileDirt(int tileId);
virtual ~TileDirt();
protected:
private:
};
When I'm trying to assign s_dirt to TileDirt I get the following error:
conversion to inaccessible base class "Tile" is not allowed
What am I doing wrong here?
I tried doing:
static const Tile* s_dirt;
And assign it like:
const Tile* Tile::s_dirt = new TileDirt(1);
But that just adds another error: no suitable constructor exists to convert from "const Tile *" to "Tile"
In C++, a variable of type Foo represents a block of memory sufficient to fit a Foo object in it (basically exactly). And it is exactly data of type Foo.
If you try to fit something else in that memory, you'll get anything from object slicing to undefined behavior (which is bad).
In many other languages (Java, C# to name two), a variable of type Foo actually represents a handle to Foo data elsewhere, or possibly a derived class of Foo. Often these are garbage collected, a non-deterministic system whereby your objects are only destroyed at some unknown point in the future after the runtime engine determines they are nominally unreachable from user code.
To emulate this in C++, you'll want a smart pointer -- std::unique_ptr<Foo> or std::shared_ptr<Foo>. These are not full on garbage collecting smart pointers, but unless you are making self-referential data, they are often good enough. One thing you have to think about in C++ is "who owns the lifetime of this data".
std::shared_ptr is often the easiest to go with.
Replace your Tile instances with std::shared_ptr<Tile>, and your call to new Tile(blah) with std::make_shared<Tile>(blah).
Next, class inheritance by default in C++ is private -- nobody is allowed to know you are a child of your parent class other than instances of your own type. If you want public inheritance, use public Tile.
Your wish to have your Tile constructor store copies of itself in a static array is a bad one. Don't do that kind of thing in the constructor, have it done outside of the constructor. (Producing a shared pointer to this is a bit annoying: the easiest method actually doesn't work well)
The error doesn't match the title of your question.
const Tile Tile::s_dirt = TileDirt(1); // This line errors
You are declaring a global constant (not macro constant, but memory allocated constant, similar to a global variable).
What about doing a test at "main" function, dropping the global stuff, and, the constant stuff, first:
int main(...)
{
Tile* MyTyle = new TileDirt();
}
And, later, if really required, change to a global member, and, a constant member ?
EDIT: This question came up and I think I aced it! Go StackOverflow!! :D
I have exams coming up, and one of the questions on last year's exams was to spot the problem with implementation of the following constructor and to write a corrected one.
Rectangle::Rectangle(string col, int len, int br)
{
setColour(col);
length =len;
breadth=br;
}
The class definitions are as follows:
class Polygon
{
public:
Polygon(string col="red");
void printDetails(); // prints colour only
virtual double getArea()=0;
void setColour(string col);
private:
string colour;
};
class Rectangle : public Polygon
{
public:
Rectangle(string, int, int);
void printDetails(); // prints colour and area
// for part 3, delete the line below
double getArea();
private:
int length;
int breadth;
};
I've written the code into the compiler and it runs fine. I'm guessing the question is relating to inheritance, since string colour; is private, but setColour is public so I cant figure it out. Unless its Rectangle::Rectangle(string col, int len, int br):length(len), breadth(br) and then set the colour inside the construcor or something.
Its only worth 3 marks so its not that big a deal if nobody wants to answer, but I figure if I'm going to have a career as a programmer, its in my interest to know as much as possible. ;)
Thanks for any help.
PS, see getArea() in Rectangle. When I remove that it tells me it "cannot instantiate the abstract class". What does that mean?
EDIT: Here's the main:
void main (void)
{
Rectangle rect1 ("blue",5,6);
Rectangle *prect2 = new Rectangle("red",5,6);
rect1.setColour("red");
rect1.printDetails();
prect2->printDetails();
}
I don't see anything wrong, though you could make it more efficient by using an initialization list (otherwise your private members of both classes get initialized twice):
Rectangle::Rectangle(string col, int len, int br)
: Polygon(col), length(len), breadth(br)
{
}
Notice that the initialization list can call the constructor of Polygon as well.
See Why should I prefer to use member initialization list? for a complete description of the advantages of using initialization lists.
If it's about best C++ practices, then:
Pass string parameters by const reference;
Use initializer list and initialize colour by passing it to parent constructor, not setColour.
The only thing I see off the bat is there is two printDetails() but the base class one is not virtual so you would not get the polymorphic behavior expected.
The main "issue" I see (and it is kinda minor) is that the derived constructor lets the parent class use its default colo(u)r value ("red"), and then supplies its own. That's kinda wasteful, when you could have given it the correct one from the get-go.
Rectangle::Rectangle(string col, int len, int br) : Polygon(col) {
length =len;
breadth=br;
};
Now, having done the above, you might as well intialize all the members that way:
Rectangle::Rectangle(string col, int len, int br)
: Polygon(col), length(len), breadth(br) {};
Hmmm. Now that I look at this, there's another thing wrong with it. Your constructors are passing in std::string objects by copy, and not modifying them. That's a waste too. All the constructor string parameters ought to be changed to string const & parameters. This potentially avoids an extra copy construction of a string at runtime, and notifies the compiler and the users that you aren't actually modifying the input strings (which is good practice when you in fact aren't).
So the final version would look more like:
Rectangle::Rectangle(string const & col, int len, int br)
: Polygon(col), length(len), breadth(br) {};
This formulation takes you from 4 std::string constructions (and 3 destructions) for every Rectangle constructor called down to 2. It can be further taken down to one by making the same change to the Polygon constructor.
You should call the base constructor with the col parameter:
Rectangle::Rectangle(string col, int len, int br) : Polygon(col)
{
//setColour(col);
length =len;
breadth=br;
}
Concerning the getArea():
The reason it doesn't compile when you remove it is because that function is marked as pure virtual in your Polygon class virtual double getArea()=0; using the =0;
For your PS regarding Rectangle::getArea(): the declaration in Polygon of virtual double getArea()=0; means that the function is a pure virtual function. You can think of this conceptually: "All polygons have an area, so I should be able to ask what it is, but unless the polygon has a particular type (square, circle), it won't know what its area is".
What this means is that your Polygon class is an abstract class. By not defining getArea() in the Rectangle class, your rectangle class is also an abstract class. You can't instantiate a Rectangle because the compiler doesn't know about any Rectangle::getArea() function definition.
You can also add call to the base class constructor in your initializer list:
Rectangle::Rectangle(string col, int len, int br)
: Polygon(col), length(len), breadth(br)
That uses the base class' constructor, so is a bit neater.
I can think of a number of possible problems here:
Use initializer lists to assign the values.
Call the base class constructor to set the color.
A string might not be the best type to represent a color. Maybe an enum or a separate color class would be better here. This also prevents passing invalid colors, if properly done.
Speaking of invalid values: length and breadth should be validated in the constructor (you don't want to end up with negative areas, do you?). At least use an assertion. It has no effect on release builds, but prevents development errors. For a more public interface, exceptions may also be an option (this is personal taste to some degree).
If you really want to use a string for the color, pass it by const reference (and probably test for edge cases like the empty string).
printDetails should probably be virtual, so you can call it with a base class pointer. The current implementation might not behave as intended.
The class seems to be designed for polymorphism. A virtual destructor has to be defined, if deletion from a base class pointer is required. Since there already is a virtual method, it probably won't hurt either.
getArea and printDetails should be declared const, so that they can be called on const objects. They shouldn't modify the object.
This is just a list of possibilities. Many of them depend on the intended usage of the class and might not be needed, but it doesn't hurt to consider them carefully.
As mentioned printDetails won't behave as expected.
I also think that just declaring getArea() within Rectangle class is kinda cheating because you do not provide implementation for it, and if you happen to call it within you code you would get a linker error.
An initialization order issue is possible. Polygon::setColour could call the pure virtual Polygon::getArea. (There is no indication that it would need to, but the possibility exists.) The implementation in Rectangle would presumably need length and breadth to compute the area, but they are not initialized yet.
The minimal fix is to initialize length and breadth before calling setColour:
Rectangle::Rectangle(string col, int len, int br)
{
length =len;
breadth=br;
setColour(col);
}
It would be best, of course, to drop the pure virtual getArea() declaration from Polygon because it doesn't appear to be needed by any Polygon methods. But that is outside of the scope of the question.