虚函数在 Direct3D 中的作用

上两篇关于"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;
}

子类 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;
}

另外, 也未必一定要使用 纯虚函数, 也可 变换成 普通的 虚函数处理, 但此处不做处理了.

 

posted @ 2012-11-29 02:09  Wilson-Loo  阅读(433)  评论(1编辑  收藏  举报