上两篇关于"Dx11DemoBase"基类的设计 和 使用过程中, 都是用了 虚函数/多态 方式, 如
Dx11DemoBase::LoadContext(), Dx11DemoBase::UnloadContext() //等等, 在实际使用过程中使用 多态方式: // class Dx11DemoSub : public Dx11DemoBase{...} Dx11DemoBase *obj = new Dx11DemoSub(); obj->doSomeThing(); obj->Shutdown(); delete obj;
就我当前的代码阅读量来说, 还是比较 赞许这方式的.
第一, 整个Direct3D 应用, 尤其是初始化过程, 涉及到太多的 资源 和 处理过程.
资源的管理是很好重要的, 而C++的封装管理机制 更够很好的完成该工作; 初始化过程中涉及的处理过程一般都只是集中在 单次的初始化阶段完成, 后续运行过程中并没有相同的设置等, 即完成单次作业. 这里选择使用 C++ 的基类来管理 基本数据 和 初始化过程.
第二, 那么采用普通的 子类 继承 基类 的方式来时现具体项目的 Direct3D开发, 不行吗? 行, 但那样会 将代码撰写 偏向于 面向对象编程, 而非面向接口编程. 如果使用下面 简单的 面向对象 编程:
#include <iostream> using namespace std; class Dx11DemoBase { public: Dx11DemoBase() { // do some initialize initialize(); } ~Dx11DemoBase() { // release all the valid resouce, // some maybe pointers to resouce releaseResource(); } void initialize() { std::cout << "Base::Construction initializing base resouces +++" << std::endl; hInstance_ = 1; hwnd_ = 2; width_ = 3; height_ = 4; swapChain_ = 5; device_ = 6; context_ = 7; otherMembers_ = 8; } void releaseResource() { std::cout << "Base::Destruction releasing base resources ---" << std::endl; hInstance_ = 0; hwnd_ = 0; width_ = 0; height_ = 0; swapChain_ = 0; device_ = 0; context_ = 0; otherMembers_ = 0; } protected: int hInstance_; int hwnd_; int width_; int height_; int swapChain_; int device_; int context_; int otherMembers_; }; class Dx11DemoSub : public Dx11DemoBase { public: Dx11DemoSub() { // base construction function Dx11DemoBase::Dx11DemoBase() will automatly called, the initialize too. std::cout << "Sub::Construction do nothing +++ +++" << std::endl; } ~Dx11DemoSub() { std::cout << "Sub::Destruction do nothing --- ---" << std::endl; } }; int main( int argc, char* argv[]) { Dx11DemoSub *subobj = new Dx11DemoSub(); std::cout << "\nDo some main loop \n" << std::endl; delete subobj; return 0; }
![](https://i-blog.csdnimg.cn/blog_migrate/05f015ad89edd8faeec093bae08e205b.jpeg)
子类 Dx11DemoSub 通过构造函数 自动调用 基类 Dx11DemoBase的构造函数, 自动进行 基本资源的创建/初始化, 相关的过程 initialize() / releaseResources() 也能够使用到. 但应该明确, 此时的 Dx11DemoSub 是一个相当"空壳"的子类, 没有自己实际的 内部成员数据, 仅仅完全对基类的"包装". 假如有实际类 DrawFruits :
class DrawFruits : public Dx11DemoBase { public : DrawFruits(); ~DrawFruits(); private: int apples; int pears; int nuts; int and_Pigs; }
为了 对 资源( apples, pears, nuts, and_Pigs) 进行创建管理, 必须在 DrawFruits::DrawFruits() 进行添加代码, 析构函数时也得进行资源的释放:
class DrawFruits : public Dx11DemoBase { public : DrawFruits() { initializeFruits(); } ~DrawFruits() { releaseFruits(); } void initializeFruits() { std::cout << "DrawFruits::initializeFruits creating my-private-resouces +++ +++" << std::endl; apples = 14; pears = 13; nuts = 12; and_Pigs = 11; } void releaseFruits() { std::cout << "DrawFruits::releaseFruits releasing my-private-resources --- ---" << std::endl; apples = 0; pears = 0; nuts = 0; and_Pigs = 0; } private: int apples; int pears; int nuts; int and_Pigs; };
对, 没错, 子类DrawFruits 的实例, 确实也能够创建资源, 并且在 实例析构时能够进行 本地资源的释放; 但应该看到, 由于基类Dx11DemoBase是我写的, DrawFruits 也是我写的, 整个继承关系中需要注意的关键点 我自己也很清楚( 就本来说, 就是子类的构造函数中进行 私有资源的 创建维护, 在析构时不要忘了释放相关资源)。 万一 其他同学 也是使用我的基类 Dx11DemoBase 呢?我们班的铜须毕业后, 有近1/3同学没有再从事 IT 行业的工作, 有位同学还回家 养农场去了, 向我借了 Dx11DemoBase , 想实现 农业现代化:
class FeedingAnimals : public Dx11DemoBase { public : FeedingAnimals() { initializeAnimals(); } ~FeedingAnimals() { } void initializeAnimals() { std::cout << "FeedingAnimals::initializeAnimals creating my-private-resouces +++ +++" << std::endl; chickens = 14; dogs = 13; cats = 12; and_Pigs = 11; } void releaseAnimals() { std::cout << "this animal-releasing function should be called." << std::endl; std::cout << "FeedingAnimals::releaseAnimals releasing my-private-resources --- ---" << std::endl; chickens = 0; dogs = 0; cats = 0; and_Pigs = 0; } private: int chickens; int dogs; int cats; int and_Pigs; }; int main( int argc, char* argv[]) { FeedingAnimals *animals = new FeedingAnimals(); std::cout << "\nDo some main loop \n" << std::endl; delete animals; return 0; }
编译成功, 但更糟糕的是, 运行起来也没有问题, 不报错, 不崩溃, 鸡不鸣, 狗不跳, “万事安宁”:
但日子久了发现, 放羊了 100只鸡, 10天后就少了10只。 只是因为在 ~FeedingAnimals() 时没有 进行资源释放
releaseAnimals() , 导致资源泄露。 问题本质在于他没有像我这般对整个流程清晰明白, 知道关键点, 更何况可能他 连
releaseAnimals()都没写呢! 万一资源更多, 处理方式很复杂呢! 很多 同学也使用 Dx11DemoBase, 但都因为不明了/或者 忘记了我殷殷切切提供的 说明书呢!
这就需要 多态提供的面向接口编程, 这种方式 提供了一种 “编码方式/机制/约定/规范”, 一旦用户(诸如我的农场同学)玩家/或压根没有遵守这种 “编码方式/机制/约定/规范”, 就会马上知道, 在编译期间就得到通知说, 违反了守则, 不修改就不能进行下一步:
#include <iostream> using namespace std; class Dx11DemoBase { public: Dx11DemoBase() { } ~Dx11DemoBase() { } void initialize() { std::cout << "Base::Construction initializing base resouces +++" << std::endl; hInstance_ = 1; hwnd_ = 2; width_ = 3; height_ = 4; swapChain_ = 5; device_ = 6; context_ = 7; otherMembers_ = 8; Must_Call_init(); } void release() { std::cout << "Base::Destruction releasing base resources ---" << std::endl; hInstance_ = 0; hwnd_ = 0; width_ = 0; height_ = 0; swapChain_ = 0; device_ = 0; context_ = 0; otherMembers_ = 0; Must_Call_release(); } virtual void Must_Call_init() = 0; virtual void Must_Call_release() = 0; protected: int hInstance_; int hwnd_; int width_; int height_; int swapChain_; int device_; int context_; int otherMembers_; }; class FeedingAnimals : public Dx11DemoBase { public : FeedingAnimals() { } ~FeedingAnimals() { } private: int chickens; int dogs; int cats; int and_Pigs; }; int main( int argc, char* argv[]) { Dx11DemoBase *animals = new FeedingAnimals(); std::cout << "\nDo some main loop \n" << std::endl; delete animals; return 0; }
在 基类Dx11DemoBase 声明了两个 纯虚函数 Must_Call_init() 和 Must_Call_release() 用于子类 使用自己的 资源时, 创建资源 和 销毁资源, 而 子类 FeedingAnimals 为实现 这两个函数, 而编译出错:
dx11demo_animals.cpp: In function `int main(int, char**)': dx11demo_animals.cpp:100: error: cannot allocate an object of type `FeedingAnimals' dx11demo_animals.cpp:100: error: because the following virtual functions are abstract: dx11demo_animals.cpp:46: error: virtual int Dx11DemoBase::Must_Call_init() dx11demo_animals.cpp:47: error: virtual int Dx11DemoBase::Must_Call_release()
实现了 这两个纯虚函数后:
#include <iostream> using namespace std; class Dx11DemoBase { public: Dx11DemoBase() { } ~Dx11DemoBase() { } void initialize() { std::cout << "Base::Construction initializing base resouces +++" << std::endl; hInstance_ = 1; hwnd_ = 2; width_ = 3; height_ = 4; swapChain_ = 5; device_ = 6; context_ = 7; otherMembers_ = 8; Must_Call_init(); } void releaseResource() { std::cout << "Base::Destruction releasing base resources ---" << std::endl; hInstance_ = 0; hwnd_ = 0; width_ = 0; height_ = 0; swapChain_ = 0; device_ = 0; context_ = 0; otherMembers_ = 0; Must_Call_release(); } virtual void Must_Call_init() = 0; virtual void Must_Call_release() = 0; protected: int hInstance_; int hwnd_; int width_; int height_; int swapChain_; int device_; int context_; int otherMembers_; }; class FeedingAnimals : public Dx11DemoBase { public : FeedingAnimals() { } ~FeedingAnimals() { } void Must_Call_init() { std::cout << "FeedingAnimals::initializeAnimals creating my-private-resouces +++ +++" << std::endl; chickens = 14; dogs = 13; cats = 12; and_Pigs = 11; } void Must_Call_release() { std::cout << "this animal-releasing function should be called." << std::endl; std::cout << "FeedingAnimals::releaseAnimals releasing my-private-resources --- ---" << std::endl; chickens = 0; dogs = 0; cats = 0; and_Pigs = 0; } private: int chickens; int dogs; int cats; int and_Pigs; }; int main( int argc, char* argv[]) { Dx11DemoBase *animals = new FeedingAnimals(); animals->initialize(); std::cout << "\nDo some main loop \n" << std::endl; animals->releaseResource(); delete animals; return 0; }
创建时, 基类的 特定资源 初始化完成后, 子类的 特定资源 也初始化了;
析构时, 子类的 特定资源释放了, 子类的 特定资源也释放了.
但是, 释放资源时, 顺序是错的, 可从 "纯虚函数 与 析构函数", "析构函数 与 多态" 等方面进行优化; 这里暂不作处理.
使用这种方式进行编码, 也是有要求的, 如:
int main( int argc, char* argv[]) { // 1. 使用多态方式 创建对象 Dx11DemoBase *animals = new FeedingAnimals(); // 2. 使用基类提供的初始化函数 animals->initialize(); std::cout << "\nDo some main loop \n" << std::endl; // 3. 使用 基类提供的释放资源函数 animals->releaseResource(); delete animals; return 0; }
另外, 也未必一定要使用 纯虚函数, 也可 变换成 普通的 虚函数处理, 但此处不做处理了.