質問

In C++, I often (almost always) run into problems with constructors; I'm never sure how to use the parameters, and eventually I'll end up using only parameterless constructor for each class. Then I'll use setters after each instance definition. For example:

// Obviously better
Point p(5, 3);

// ...and yet I end up using this
Point p;
p.setX(5);
p.setY(3);

The reason I end up using the "worse" method is because some classes take so many parameters and there are so many different possibilities to costruct them, so my thoughts get messed up and I no longer know what I should do. Here's an example of a recent class I made (it uses Qt):

// implements position and pixmap (image shown when painted)
class Block : QObject {
    public:
        Block(const QPixmap &img = Pixmap(), QObject *parent = 0);
        Block(const QPoint &pos, const QPixmap &img = Pixmap(), QObject *parent = 0);
        Block(int x, int y, const QPixmap &img = Pixmap(), QObject *parent = 0);
        Block(const Block &other);
};

Having only one class with only two attributes (position and image), I already get four constructors taking four parameters at most. Also, I often see people replacing the first constructor with Block(QObject *parent = 0); and Block(const QPixmap &img, QObject *parent = 0); and I'm not sure if this would be a good thing to do.

Now then, what if I craete a Rectangle class of my own, which inherits from Block so it also needs to take image and position as it's parameters:

Rect(const QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(const QRect &rect, const QPixmap &pixmap = QPixmap(), QObject *parent = 0);
Rect(const QSize &size, const QPoint &pos = QPoint(), const QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(const QPoint &pos, const QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(int x, int y, int w = 1, int h = 1, QPixmap &img = QPixmap(), QObject *parent = 0); 
Rect(int w, int h, QPixmap &img = QPixmap(), QObject *parent = 0); 
Rect(const QPoint &pos, int w, int h, QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(int x, int y, const QSize &size, QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(const Rect &rect);

As you see, it's starting to look terrible and damn hard to maintain. Just imagine Player class inheriting from Rect and implementing items, health, mana, damage, armor... I find it impossible to code such constructors.

So now that I've given you the problem, I'm not sure of the question; I need any tips or tricks to help me with my constructos, when to use default parameters and how often do I need all the constructors and yeah, just some tips to prevent me from creating 500 constructors for one class; should I just go with my original p.setX(5); example?

役に立ちましたか?

解決

If you are creating your own classes, you should definitely avoid this exponential explosion of constructor combinations. One way to do that is to provide other mechanisms to create instances of the class. There are several general Creational Patterns that can fill this purpose for a variety of specific needs. Design Patterns is a huge topic in its own right, so I suggest you find some reading material to help you along the way.

Also, you probably should revisit your design. Often when you have a lot of parameters for a constructor or several constructors with a wide variety of parameters, it indicates that the class is responsible for too many disparate things. Try refactoring the class into smaller, more manageable chunks, each of which is responsible for a single task. (See Single Responsibility Principle.)

他のヒント

There has to be a balance between simplicity and usability. If you have 500 constructors then someone will have to wade through a lot of documenation to find the one that they need to use. You will also have to create a lot of test code to ensure that all of them work as expected. A simple rule of thumb is -"What is the least that I can expose, that is the most beneficial"?

Version your classes, and then create the simplest interface that someone else can use.

For your example with the two attributes - block and position - it is not up to you to provide to much choice to a user of the class by offering four constructors. Give one version of the constructor - and whomever uses the class will know what to do - given their particular circumstance.

Taking this section as an example, it can be greatly simplified:

    Block(const QPixmap &img = Pixmap(), QObject *parent = 0);
    Block(const QPoint &pos, const QPixmap &img = Pixmap(), QObject *parent = 0);
    Block(int x, int y, const QPixmap &img = Pixmap(), QObject *parent = 0);

Remove these two:

    Block(const QPixmap &img = Pixmap(), QObject *parent = 0);
    Block(int x, int y, const QPixmap &img = Pixmap(), QObject *parent = 0);

If you want it at "no position", you just give it an empty QPoint:

    Block(QPoint(), img); 

And if you want it at x, y, then make a QPoint(x, y)

    Block(QPoint(x, y), img, parent); 

(You could of course make it QPoint pos = QPoint(), so that if you want an empty one you can use Block().

The same principle can be applied to Rect, you have several variants that are "nearly the same". Replace all the variations with one that takes a QRect as the first argument. If you don't happen to "have" a QRect, surely one can be created as a temporary (and in most cases, that will just "disappear").

Hopefully, this ALSO makes the set actual constructor simpler, since you can just do

m_rect = rect; 

rather than having to write 6 different forms of "I have some different items, that need to be made into a rect".

Unless there is a VERY good reason to add another constructor, don't. Just because the code you are working on right now happens to have x, y, h, and w, doesn't mean you can't make it into a QRect pretty easily.

Aggregating initializers into a single structure is often useful, although this isn't always feasible. Otherwise, I agree with the first response - try to simplify things into logical groups and such.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top