Overriding static variables when subclassing
我有一个类,我们称之为A,在这个类定义中,我有以下内容:
1 | static QPainterPath *path; |
也就是说,我声明了一个指向路径对象的静态(类范围)指针;这个类的所有实例现在都将具有相同的共享数据成员。我希望能够在这个类的基础上进行构建,将其子类化为更专业的形式、分层行为,并且每个类都有自己独特的路径对象(但不必重复计算边界框或调用绘图例程等无聊的部分)。
如果我将其子类化以创建一个类F(例如),我希望F使用继承自A的绘图例程,但使用在F中声明的静态(类范围)路径对象。我尝试将上述声明放在私有节中(并在派生类F中重复),并尝试将其放在受保护节中,所有这些都没有任何乐趣。
我可以看出这是为什么:
1 2 | void A::paint() { this->path... |
引用的是::路径而不是f::路径,即使对象属于F类。
有没有一种优雅的方法来绕过这个问题,并允许每个类维护一个静态路径对象,同时仍然使用在基类中定义的绘图代码,并且让所有类(除了基类)都是真实的和可实例化的?
使用虚拟方法获取对静态变量的引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Base { private: static A *a; public: A* GetA() { return a; } }; class Derived: public Base { private: static B *b; public: A* GetA() { return b; } }; |
注意b是从a派生出来的。然后:
1 2 3 | void Derived::paint() { this->GetA() ... } |
您可能可以对混合模板模式或奇怪的重复模板模式执行变体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include <stdio.h> typedef const char QPainterPath; class Base { public: virtual void paint() { printf("test: %s ", getPath() ); } virtual QPainterPath* getPath() = 0; }; template <class TYPE> class Holder : public Base { protected: static QPainterPath* path; virtual QPainterPath* getPath() { return path; } }; class Data1 : public Holder<Data1> { }; class Data2 : public Holder<Data2> { }; template <> QPainterPath* Holder<Data1>::path ="Data1"; template <> QPainterPath* Holder<Data2>::path ="Data2"; int main( int argc, char* argv[] ) { Base* data = new Data1; data->paint(); delete data; data = new Data2; data->paint(); delete data; } |
我刚刚在代码块中运行了这段代码,得到了以下信息:
1 2 3 4 5 | test: Data1 test: Data2 Process returned 0 (0x0) execution time : 0.029 s Press any key to continue. |
我没有测试过这个,但是引入了一个虚拟函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | struct Base { void paint() { APath * p = getPath(); // do something with p } virtual APath * getPath() { return myPath; } static APath * myPath; }; struct Derived : public Base { APath * getPath() { return myPath; } static APath * myPath; }; |
可能是你想要的。注意,您仍然需要在某个地方定义这两个静态:
1 2 | APath * Base::myPath = 0; APath * Derived::myPath = 0; |
您可以使用虚拟函数来实现您的结果。这可能是你最干净的解决方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class A { protected: virtual QPainterPath *path() = 0; private: static QPainterPath *static_path; /* Lazy initalization? */ }; QPainterPath *A::path() { return A::static_path; } class F : public A { protected: virtual QPainterPath *path() = 0; private: static QPainterPath *F_static_path; /* Lazy initalization? */ }; QPainterPath *A::path() { return F::F_static_path; } |
我知道这个问题已经被回答了,但是有另一种方法可以通过一个助手类和一些模板专门化为多个类设置类似静态变量的值。
它并不能完全回答这个问题,因为它与子类化没有任何联系,但是我遇到了同样的问题,我找到了一个我想要分享的不同解决方案。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | template <typename T> struct Helper { static QPainterPath* path; static void routine(); } // Define default values template <typename T> QPainterPath* Helper<T>::path = some_default_value; template <typename T> void Helper<T>::routine { do_somehing(); } class Derived {}; // Define specialized values for Derived QPainterPath* Helper<Dervied>::path = some_other_value; void Helper<Dervied>::routine { do_somehing_else(); } int main(int argc, char** argv) { QPainterPath* path = Helper<Derived>::path; Helper<Derived>::routine(); return 0; } |
赞成的意见:
- 清除,编译时初始化
- 静态访问(无实例化)
- 您也可以声明专门的静态函数
欺骗:
- 没有虚拟化,您需要准确的类型来检索信息
如果您不关心外观,只需在使用path之前使用a::或f::来选择正确的外观,或者如果您不喜欢::以不同的方式命名它们。
另一个选项是使用函数将其清除,例如,在a和f中的virtual qpainterPath*getPath()返回a::path;和qpainterPath*getPath()返回f::path;。
实际上,尽管这个问题只是关于代码的外观,而不是它的功能,而且由于它并没有真正改变可读性,所以我不会担心这个…
您可能不希望静态变量被重写。也许你可以在你的类中存储一个指针?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class A { public: A() : path(static_path) { } protected: A(QPainterPath *path) : path(path) { } private: QPainterPath *path; static QPainterPath *static_path; /* Lazy initalization? */ }; class F : public A { public: F() : A(F_static_path) { } private: static QPainterPath *F_static_path; /* Lazy initalization? */ }; |
您不能"重写"静态函数,更不用说静态成员变量了。
您需要的可能是一个虚拟函数。这些只能是实例函数,因此没有类实例就无法访问它们。