C++: Making method return the same object every time
I would like to have a function that loads a file (in this case an OpenGL texture), but only act开发者_如何转开发ually loads the file once, and every time it is called after that it just returns what it initially loaded.
What would be a good way to do this?
Thanks.
You need some place to store that state. That can be either inside an object or as a static variable. Let's say:
class TextureLoader {
public:
TextureLoader() {}
GLuint loadTexture(std::string const & filename){
std::map<std::string, GLuint>::iterator it = m_loadedTextures.find(filename);
if(it == m_loadedTextures.end()){
GLuint id = load(filename);
m_loadedTextures[filename] = id;
return id;
}
else{
return it->second;
}
}
~TextureLoader(){
// iterate and delete textures
}
private:
GLuint load(std::string const & filename){
// real loading
}
std::map<std::string, GLuint> m_loadedTextures;
};
One way to do this would be to have a static map inside the function that associates the parameters to the function (here, the filename) with a pointer to the unique value it returns. You can then have the function check whether the map contains the input and, if so, hand back the texture associated with it. Otherwise, you can then do the load, store the result in the map, and hand back the result. For example:
Texture* LoadTexture(const std::string& filename) {
static std::map<std::string, Texture*> previousResults;
/* Look up existing value. */
Texture* result = previousResults[filename];
/* If this doesn't exist, then go create it and pretend it was there all along. */
if (result == NULL)
result = previousResults[filename] = ActuallyLoadTexture(filename);
/* Hand back the cached result. */
return result;
}
If you do this, you should be careful about thread-safety, since multiple calls to the function could cause problems with the map. Syncrhonize as appropriate.
It sounds like what you are looking for is an implementation of the Singleton Design Pattern.
There are a variety of ways to implement this, and now that you know what it is called, you can decide what method is best. Your first stop could be a search of this site for other similar questions.
Typically, you would either associate with a map or unordered_map filepaths to Texture*s.
class render_state {
std::map<std::string, Texture> textures;
Texture* load_texture(std::string filepath) {
if (textures.find(filepath) != textures.end()) {
return &textures[filepath];
}
// load the texture here if it's not in cache
}
// Other rendering methods and state here.
};
But now, you have another problem, which is that sometimes you might use a relative filepath, or sometimes, an absolute filepath. Also, in some libraries, they can accept varying versions of newlines and backslashes or forward slashes. What if I loaded a Texture, then only used it for a specific time and didn't need it again? Whoops, memory leak.
The best thing to do is just return a Texture object (or (possibly smart) pointer to such) and let the user worry about managing it. If someone creates a resource, it's their job to manage it, not yours.
See boost::flyweight. It does pretty much what you want. Load objects avoiding duplicates.
Would this be what you are looking for:
Texture& loadTexture(cosnt std::string& texture)
{
// Store all loaded data here
// Each file maps to a loded texture object
static boost::ptr_map<std::string, Texture> data;
boost::ptr_map<std::string, Texture>::iterator find = data.find(texture);
if (find == data.end())
{
// If it is not in the structure then load it this one time
find = data.insert(texture, doLoad(texture));
}
// return a reference to the texture
return *(find->second);
}
精彩评论