Azriel
Azriel

Reputation: 386

Initialization function call whose effects are needed for the initialization list?

I have a base Image class with const field members:

class Image {
protected:
    const int width;
    const int height;

public:
    virtual ~Image();
    const int getWidth() const;
    const int getHeight() const;

protected:
    Image(const int width, const int height);
};

This means in the constructor, I have to initialize the width and height in the initialization list like so:

Image::Image(const int width, const int height) :
        width(width), height(height) {
}

Now I intend to subclass this such that I can load an image by providing the filepath to the subclass (so calling code does not have to worry about loading the image). This class would look similar to the following:

class GlImage: public Image {
private:
    const GLuint textureId;
    const int textureWidth;     // power of 2 width
    const int textureHeight;    // power of 2 height
    const double textureCoordinateX;
    const double textureCoordinateY;

public:
    GlImage(const string path); // is this constructor possible?
    virtual ~GlImage();
    const double getTextureCoordinateX() const;
    const double getTextureCoordinateY() const;

private:
    // what do we use for initialization functions?
};

However, we can see here that - the image has to be loaded before we can obtain the width/height - there are fields in the subclass that need to be initialized in the initialization list as well

How can the constructor of the subclass be set up such that all of these fields can be initialized?

It's not possible to introduce another object in the subclass which captures all of the field data, load this as the first object in the initialization list, and re-extract the values out for all the other fields as the base class needs the width/height (which aren't available if the image loading logic is in the derived class).

Is the only way out to remove the const modifiers and initialize the fields in the constructor?

Upvotes: 2

Views: 144

Answers (2)

Mankarse
Mankarse

Reputation: 40603

You could use a delegating constructor with a mutable auxiliary object that does the loading:

class GlImage: public Image {
    ...
    GlImage(const string& path) : GlImage(GlImageLoader(path))
    {
    }

private:
    struct GlImageLoader {
        int w;
        int h;
        GlImageLoader(const string& path) {
            //Read image data
            w = ...;
            h = ...;
        }
    };
    GlImage(GlImageLoader&& l) : Image(l.w, l.h), textureId(l.textureId), ...
    {
    }
};

Upvotes: 0

Zdeslav Vojkovic
Zdeslav Vojkovic

Reputation: 14581

The problem is that you can't call a base class constructor from derived class ctor initializer list, as you don't know the dimensions, right?

Why don't you create a static method on derived class?

class GlImage: public Image {
    ....
    static GlImage LoadFromFile(const string& path)
    {
         // read image data and other info
         int w = ...
         int h = ...
         return GlImage(w, h, ....) 
         // of course, you would need a different ctor in derived class
         // it can even be private
    }
 }

Upvotes: 1

Related Questions