ph4*_*t0m 3 c++ directx move-constructor directx-9
我想写一个ContentManager
为游戏加载和维护不同类型资产的类(与XNA的ContentManager相比).我的头文件如下所示:
class ContentManager
{
public:
ContentManager(Direct3D& d3d, const std::string& rootDirectory = "Resource");
~ContentManager();
template<typename T>
const T& Load(const std::string& assetName);
private:
Direct3D& d3d_;
std::string rootDirectory_;
std::map<std::string, Texture> textures_;
};
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,我有一个每种资产类型的地图(目前只针对纹理)和一个泛型Load<T>()
方法,我会为我想要存储的每种资产类型显式实例化.Load<Texture>()
从磁盘读取图像文件(如果它尚未在地图中),创建一个新的Texture
,将其插入到地图中并返回它.
我的Texture
班级基本上创建和封装了原始IDirect3DTexture9
遵循RAII
成语(析构函数调用Release()
的texture_
):
class Texture
{
public:
Texture();
Texture(Direct3D& d3d, unsigned int width, unsigned int height,
const std::vector<unsigned char>& stream);
~Texture();
IDirect3DTexture9* GetD3DTexture() const { return texture_; }
private:
IDirect3DTexture9* texture_;
};
Run Code Online (Sandbox Code Playgroud)
在测试我的代码时,我意识到每个纹理都被释放了两次,因为Texture
在某些时候创建了对象的(浅)副本,当然,每个纹理都会调用析构函数.
此外,虽然Load<T>()
返回对地图元素的引用,但调用者可以自己制作副本并触发相同的问题.我的想法:
制作Texture
私有的复制构造函数是没有解决方案的,因为在创建std::pair
向地图中插入新元素时,无论如何都需要复制.
定义创建深层副本的自定义复制构造函数似乎根本不合理,因为我需要创建底层Direct3D纹理的副本,该纹理应该只存在一次.
那么我的选择是什么?这可以通过使用智能指针来解决吗?应该在地图中存储哪种类型以及应Load<T>()
返回哪种类型?
在C++ 11中,该语言添加了一个名为moving的功能,正是因为您遇到的原因.幸运的是,修改代码以使用移动机制非常简单:
class Texture
{
public:
Texture() noexcept;
Texture(Direct3D& d3d, unsigned int width, unsigned int height,
const std::vector<unsigned char>& stream);
Texture(Texture&& rhs) noexcept
: texture_(rhs.texture_) //take the content from rhs
{rhs.texture_ = nullptr;} //rhs no longer owns the content
Texture& operator=(Texture&& rhs) noexcept
{
Clear(); //erase previous content if any
texture_ = rhs.texture_; //take the content from rhs
rhs.texture_ = nullptr; //rhs no longer owns the content
return *this;
}
~Texture() noexcept {Clear();}
IDirect3DTexture9* GetD3DTexture() const noexcept { return texture_; }
private:
void Clear() noexcept; //does nothing if texture_ is nullptr
IDirect3DTexture9* texture_;
};
Run Code Online (Sandbox Code Playgroud)
这会添加一个"移动构造函数"和一个"移动赋值",它会将 内容从一个移动Texture
到另一个,这样一次只能指向一个给定IDirect3DTexture9
的内容.编译器应该检测这两个,并停止生成隐式复制构造函数和复制赋值,这样你Texture
就不能再复制了,这正是你想要的,因为深度复制IDirect3DTexture9
很难,甚至没有意义.现在神奇地修复了Texture类.
现在,其他课程怎么样?没有变化.std::map<std::string, Texture>
足够聪明,可以检测到你的类有noexcept
移动操作符,因此它将自动使用它们而不是副本.它也使map
自身可移动但不可复制.并且由于它map
是可移动的但不可复制的,因此可自动地使ContentManager
可移动但不可复制.当你考虑它时有意义,移动内容很好,但你不想复制所有这些.所以那些不需要任何改变
Texture getter(); //returns a temporary Texture
Texture a = getter(); //since the right hand side is a temporary,
//the compiler will try to move construct it, and only
//copy construct if it can't be moved.
a = getter(); //since the right hand side is a temporary,
//the compiler will try to move assign it, and only
//copy assign if it can't be moved.
void setter1(Texture&); //receives a reference to an outside texture object
setter1(a); //works exactly as it always has
setter1(getter()); //compiler error, reference to temporary
setter1(std::move(a)); //compiler error, passing rreference && instead of lreference &.
void setter2(Texture); //receives a local texture object
setter2(a); //compiler error, no copy constructor
setter1(getter()); //creates the Texture by moving the data from the temporary
setter2(std::move(a)); //The compiler moves the content from a to the function;
//the function receives the content previously held by a, and
//a now has no content. Careful not to read from a after this.
void setter3(Texture&&); //receives a reference to a temporary texture
setter3(a); //compiler error, a is not a temporary
setter3(getter()); //function receives reference to the temporary, all is good
setter3(std::move(a)); //function thinks a is temporary, and will probably remove
//it's content. Careful not to read from a after this.
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
254 次 |
最近记录: |