Search code examples
c++classinheritancecastingstatic-variables

Trying to edit static variable of parent A class for all child class B


I have a problem with a static variable within a class. I'm trying to edit a static variable of a child class without editing the others childs class static variable.

The header file :

class A {
public:
    A() {}

    void printName() {qDebug() << _name; }    
    void changeName(QString name) {_name = name;}

private:
    static QString _name;
};
QString A::_name = QString("default");

class B : public A {
public:
    B() : A() {}
};

class C : public A {
public:
    C() : A() {}    
};

I'm trying to edit the static _name of my class B without editing the _name of my class C. When I try this code in this main.cpp :

int main(int argc, char *argv[])
{
    A *a = new B{};
    A *b = new B{};
    A *c = new C{};

    a->printName();
    b->printName();
    c->printName();

    B *tmp = dynamic_cast<B*>(a);
    tmp->changeName("new");

    qDebug() << "Then";    

    a->printName();
    b->printName();
    c->printName();
}

Here's what I have :

"default"
"default"
"default"
Then
"new"
"new"
"new"

Anyone has any idea on how I could fix this ?

Here's what I've also try :

class A {
    public:
    A() {}
    virtual ~A() {}

    void printName() {qDebug() << _name; }

    virtual void changeName(QString name) {_name = name;}
    private:
    static QString _name;
};

QString A::_name = QString("default");

class B : public A {
public:
    B() : A() {}

    void changeName(QString name) override {_name = name;}
private:
    static QString _name;
};

class C : public A {
public:
    C() : A() {}

    void changeName(QString name) override {_name = name;}
private:
    static QString _name;
};

Solution

  • There is only one A::_name, it can only have one value at any given time. Since all your derived types uses the same static member they necessarily all have the same _name value. To fix this, each derived type must provide it's own static member instead.

    To avoid repeating the same members in every derived type, you can define them in a templated intermediate class that sits between A and the derived types B and C. Each template specialization has it's own static member. So, provided each derived type supplies a unique value to the intermediate type's template argument, they will have their own names. For example, split A into two classes :

    #include <iostream>
    #include <string>
    
    class A {
    public:
        virtual void printName() = 0;
        virtual void changeName(std::string name) = 0;
    };
    
    template<class T>
    class A_impl : public A
    {
    public:
        void printName() override { 
            std::cout << _name << '\n';
        };
        void changeName(std::string name) override { 
            _name = std::move(name);
        };
    
    private:
        static std::string _name;
    };
    
    template<class T>
    std::string A_impl<T>::_name = "default";
    

    Then each derived type should inherit from A_impl instead of A. By providing their own type to A_impl, you can be sure each derived type provides a unique template argument :

    class B : public A_impl<B> { };
    
    class C : public A_impl<C> { };
    

    Now your test should print

    default
    default
    default
    Then
    new
    new
    default