Sorry to post such a specific question, but I'm confused by an example question given in college. The question is:
class BoundingBox {
private:
float m_width;
float m_height;
public:
BoundingBox(float width = 0, float height = 0) :
m_width(width), m_height(height) {}
};
class Enemy {
private:
EnemyType m_type;
BoundingBox m_box;
Enemy(BoundingBox const & box, EnemyType type = EnemyType::Boss) :
m_type(type);
m_box(box);
{}
};
Q: Is the following construction of Enemy legal? Explain.
Enemy enemy1(10);
The answer supplied says that it is, because the 10 is passed in as the width parameter and the default is used for the height, and also the default is used for the EnemyType. But from my understanding, the line:
BoundingBox const & box
is expecting a box object to be passed to it, not a parameter for it's constructor.
Has my lecturer made a mistake, or am I missing something? If I have misunderstood, could you provide me with a link that explains what is happening 'under the hood' so to speak. I didn't know this was a valid way to construct an object. I would ask my lecturer but he's been out sick for a week, and I can't find anything online about parameter based construction.
Yes, it is fine and will compile (barring the syntax and access to the constructor).
To create the type Enemy, a BoundingBox is required; in particular the Enemy constructor accepts the argument as a const& thus allowing temporary values to be used.
To create the BoundingBox, no argument, one float or two floats can be used. The variation is because default arguments are supplied in the BoundingBox constructor. The constructor is not marked explicit (and this is the key bit that makes it work), the compiler is thus allowed to implicitly create the BoundingBox temporary by itself - which it duly does and then creates the Enemy object.
Adding an explicit will result in a compilation failure; it would be constructive for you to do this, and observe the error messages you receive. I suspect this could the subject of future lectures as well.
In general and often, advice is given to mark constructors that could take a single argument (taking into account defaults) as explicit thus preventing unknown (or unseen) conversions. If the implicit conversion is required, then don't add the explicit.
Code with the syntax issues cleared up;
class BoundingBox {
private:
float m_width;
float m_height;
public:
/*explicit*/ BoundingBox(float width = 0, float height = 0) :
// ^^^^^^^ experiment with and without this being present
m_width(width), m_height(height) {}
};
class Enemy {
private:
EnemyType m_type;
BoundingBox m_box;
public:
// ^^^ added to allow access to the constructor
Enemy(BoundingBox const & box, EnemyType type = EnemyType::Boss) :
m_type(type),
// ^ syntax here
m_box(box)
// ^ syntax here
{}
};
Related
I created a simple class for calculating the area of a rectangle.
class Rectangle
{
public:
Rectangle();
Rectangle(const double, const double);
class Invalid { };
double print_square();
private:
const double length, width;
inline bool check();
};
For width and length, I used constants, because they will not be modified in my class. In the constructor, I want to initialize them.
Why do I get an error in constructor body?
Cannot assign to non-static data member 'length' with const-qualified
type 'const double'
Constructor for 'Rectangle' must explicitly initialize the const
member 'length'
Error:
Rectangle::Rectangle(const double _length, const double _width)
{
length = _length;
width = _width;
if (!check())
throw Invalid();
}
OK:
Rectangle::Rectangle(const double _length, const double _width) : length(_length), width(_width)
{
if (!check())
throw Invalid();
}
I quite like the second option, but for a more convenient readability I do not want to write all the initialized variables in one line.
Is there a way to initialize a constant in the body of the constructor?
No, you can't.
This is because in your first example, since there is nothing in the initializer list, length and width will be default constructed, and then, in the body of the constructor, assigned the needed values _length and _width, which fails because they are constand already initalized.
In your second example, both variables are initialized without being default constructed. This approach constructs the const variables directly with the needed values, in one step.
Note that approach 2 uses only one step instead of two, so it is more efficient than what you want to do in approach 1. Because of that, it is generally good practice (even for non-const variables) to go with approach 2 whenever possible.
Is there a way to initialize a constant in the body of the constructor?
No.
You must use the initializer list (your second solution):
Rectangle::Rectangle(const double _length, const double _width) :
length(_length),
width(_width)
{
if (!check())
throw Invalid();
}
As already stated in a comment, you can put each variable initialization on a separate line
I have been looking for quite some time for my question, but I haven't found any satisfying answer yet.
The question is fairly easy: What should I put between the brackets when I use Implicit Assignment for an Object.
I was always used to seeing something like this: The assign an int, you give it an int-value
class Point2D
{
private:
int m_nX;
int m_nY;
public:
// A specific constructor
Point2D(int nX, int nY)
: m_nX(nX), m_nY(nY) // So the int m_nX gets the int-value nX
{
}
}
But recently I found a tutorial where PARAMETERS were given to an object. (http://www.learncpp.com/cpp-tutorial/102-composition/)
class PersonalComputer
{
private:
CPU m_cCPU;
Motherboard m_cMotherboard;
RAM m_cRAM;
public:
PersonalComputer::PersonalComputer(int nCPUSpeed, char *strMotherboardModel, int nRAMSize)
: m_cCPU(nCPUSpeed), m_cMotherboard(strMotherboardModel), m_cRAM(nRAMSize)
// So the m_cCPU-object is given a parameter nCPUSpeed, and not another m_cCPU-object
{
}
};
So my question is: How does Implicit Assignment work for objects? And how can I assign an Object with an Object itself (e.g. give an m_cCPU2-object to the m_cCPU-object).
Thanks
This is called initialization (not "implicit assignment").
For a primitive type it means that object is given that initial value
int nX(5);
For a class type it means to call the class's constructor with those arguments:
Motherboard m("ABCDE");
To assign an initialize an object with another object in the initializer list you want to define a copy constructor
class CPU
{
CPU(const CPU& other)
: someSpeedVariable(other.someSpeedVariable) // Eg.
{}
}
Then you can do this in other classes.
class PC
{
PC(const CPU& cpu)
:m_cpu(cpu)
{}
CPU m_cpu;
}
I think this is what you're asking?
Sorry I'm pretty green with C++ is this sort of thing possible?
I have a superclass that has a huge constructor so I made a static factory type constructor that will do some work and then return new ParametricShape(blah blah blah...);
class ParametricShape: public ModelView{
public:
//actually has 15 arguments didn't want to type them all up
ParametricShape(func x, funcy, funcz, float lu, float hu, float lv, float hv, int sv, int su);
static ParametricShape* makeDonutShape(float);
}
Later I want to use this static method in an extended class.
class Donut : public ParametricShape{
Donut();
}
Donut::Donut(){
this = ParametricShape::makeDonut(1.0f);
}
This is the sort of thing I'm trying to do, I've been fumbling around with various copy constructors and what not and been getting all sorts of different errors. Currently it just says: lvalue requried as left operand of assignment. Thanks for any help!
You can not assign to "this". It is a const type.
The type of the this pointer for a member function of a class type X,
is X* const.
In the factory design pattern you usually have a separate class taking care of creating new instances. This separation of object creation from the class itslef is the whole point of this design pattern. See this discussion on how to implement this in C++
Here's the typical "factory" pattern:
#include <memory>
struct Shape
{
enum Type { Donut, Sphere, Teapot };
static std::unqiue_ptr<Shape> make(Type type);
virtual ~Shape() = default;
// ...
};
struct Donut : Shape
{
// ...
};
std::unique_ptr<Shape> Shape::make(Shape::Type type)
{
switch(type)
{
case Donut: return { new Donut; }
default: return { };
}
}
Usage:
auto p = Shape::make(Shape::Donut);
While I agree with most of the other answers, in your case you don't need the classical factory pattern. As I can see, you want to have a single class which describes a shape with parametric functions for their geometry. You want to provide convenience methods to create those shapes for some special cases, like the "donut". As long as you don't want to make use of any other inheritance features like overloading some methods for special shape types, you can simply drop your other classes like Donut and just use the "maker" functions in your client code.
An option to make the code still look more object-oriented (and to keep the subclasses and use their constructors in the client code), you can rewrite the maker functions to initialization functions, as I want to call them. Note that I also introduced a default constructor which we need in the specialized classes:
class ParametricShape {
func x, y, z;
...
public:
ParametricShape();
ParametricShape(func x, func y, func z, ...);
protected:
void initDonutShape(float);
};
class Donut : public ParametricShape {
public:
Donut(float radius) :
ParametricShape() // call default constructor of ParametricShape
{
initDonutShape(radius); // initialize functions for "Donut"
}
};
Now, implement the initDonutShape method like this:
void ParametricShape::initDonutShape(float radius) {
// set the parametric functions
x = ...
y = ...
z = ...
}
rather than returning a new instance of ParametricShape.
yes, it is possible... :)
#include <new>
Donut::Donut() {
this->~Donut();
new (this) ( *((Donut*) ParametricShape::makeDonut(1.0f)) );
}
make sure you have a copy-constructor defined,
this->~Donut(); ensures that everything allocated via default constuctors are deleted, and the second line new (this) ( (Donut*) ParametricShape::makeDonut(1.0f) ); first creates a donut object, reinterprets it as Donut [be careful, this is problemless here because Donut does not define any variable more than it's parent] and calls a copy-constructor...
new (this) will not allocate any new storage from memory, it just calls the constructor :)
When it comes to creating classes I've been told NO public data members. That's fine and I understand why but here's my issue. I've got a struct that I am using in a class (Linear linked list). I'd really like to initialize one of the struct fields (an array) to NULL so that I can check that the client is passing me valid structs, not uninitalized one's that are full of garbage values. I realize c++ won't let me set a default value in the struct declaration, so how do I protect from the client giving me garbage?
struct task {
char *description;
char *name;
float time_remaining;
float time_spent;
};
Then in my class I'm trying to do something like:
//I am making a copy of "to_add" and storing it in the LLL
int list::add_to_list(const task & to_add)
{ /.../ }
I don't want the user adding a bunch of uninitialized "task's" to the linked list... What to do? When I turned that struct into a class and moved the data members to private I had a TON of issues trying to access the data members to make copies of them. I was very careful not to do anything to effect the value of the var's but I couldn't get away from the compiler giving me errors about "error: passing ‘const task’ as ‘this’ argument of ‘char* task::get_comp_name()’ discards qualifiers [-fpermissive]" ("get_comp_name") was one of the getter's that I was sure wasn't editing any values, just passing me a copy) Please help me before I shoot myself. In the face.
In C++ a struct and a class are the same except for access control. So the struct's default access to members and inheritance is public, whereas the class' one is private. So you can give your struct a defult constructor and others to initialize it.
struct task {
task() : desctiption(0), name(0), time_remaining(0.), time_spent(0.) {}
char *description;
char *name;
float time_remaining;
float time_spent;
};
One side-effect of adding a constructor is that the struct isn't an aggregate anymore. This may or may not be an issue for you.
In C++11, you are also able to initialize members at the point of declaration:
struct task {
char *description{nullptr};
char *name{nullptr};
float time_remaining{0};
float time_spent{0};
};
This in-place initialization accepts type x = y syntax too, and argument-less {} initialization results in value initialization, which results in zero initialization for primitive types, so the arguments in the example could have been omitted.
There are several issues at hand here.
Public or not Public ?
Public attributes can seem handy, but they usually come back to bite you when you least expect it. I already suspect an issue with the time_remaining and time_spent: I suppose that both are modified quite at the same moment, aren't they ?
By default, variable attributes should be private, so that the class may enforce invariants such as time_remaining + time_spent is a constant throughout the lifetime of the task.
It is fine for constant attributes to be public, their role in invariants is settled once and for all in the constructor anyway.
But the weird errors message ?
This is because you lack a good tutorial or book. The problem at hand is quite simple: it is a const-ness related issue.
A const object may only be passed by const-reference or value to functions/methods and only const methods may be called on it. In your case, the proper declaration of the name() method should have a const qualifier after the method name.
Putting it altogether
And throwing in std::string because it's so much easier to manipulate.
class Task {
public:
Task(): time_remaining(0), time_spent(0) {}
Task(std::string name, std::string desc, float duration):
_name(name), _desc(desc), _time_remaining(duration), _time_spent(0) {}
// Accessors
std::string const& name() const { return _name; }
std::string const& description() const { return _desc; }
float time_remaining() const { return _time_remaining; }
float time_spent() const { return _time_spent; }
// Modifiers
void spend(float duration) {
_time_remaining -= duration;
_time_spent += duration;
}
private:
std::string _name, _desc;
float _time_remaining, _time_spent;
}; // class Task
Note: it could probably be beneficial to check that the duration passed to the spend method is not superior to the _time_remaining attribute, otherwise you spend more than you have...
Implement a constructor for the struct:
struct task {
task() : description(0), name(0), time_remaining(0), time_spent(0) {}
char *description;
char *name;
float time_remaining;
float time_spent;
};
The only difference between a class and a struct in C++ is the default accessibility of it's members.
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.