in-class 内联非静态字段初始化+对象池->减少maintainability/readability
in-class in-line non-static field initialization + object pool -> decrease maintainability/readability
要提高创建和销毁对象时的性能,可以使用池化。
在某些情况下,我不想深入研究自定义分配器或 char[]
等低级技术。
另一种方法是创建对象池.
但是,此技术不适用于 in-class 字段(内联)初始化。
起初,我根本不认为这是个问题。
但是,这个图案反复出现一百次,我想我应该有一些对策。
示例
假设我的程序的第一个版本是这样的:-
class Particle{
int lifeTime=100; //<-- inline initialization
//.... some function that modify "lifeTIme"
};
int main(){
auto p1=new Particle();
delete p1;
//... many particle created & deleted randomly ...
};
采用对象池后,我的程序可以是:-
class Particle{
int lifeTime=100; //<---- maintainability issue
void reset(){
lifeTime=100; //<---- maintainability issue
}
};
int main(){
auto* p1=pool.create();
//.... "Particle::reset()" have to be called somewhere.
};
重复代码会导致一些可维护性问题。
问题
如何在不牺牲代码可维护性和可读性的情况下将对象池应用于具有内联字段初始化的现有对象?
我目前的解决方法
我一般让构造函数调用reset()
.
class Particle{
int lifeTime;
public: Particle(){
reset(); //<---- call it here, or "pool" call it
}
void reset(){
lifeTime=100;
}
};
缺点:与旧的内联初始化相比,它降低了代码的可读性:-
int lifeTime=100;
对不起,如果这个问题太初学者了,我是C++的新手。
这是 std::unique_ptr<>
的常用用例:
class Base {
static constexpr int lifespan = 100;
int lifetime = lifespan;
public:
void reset() noexcept { lifetime = lifespan; }
}
struct Deleter {
void operator ()(Base* const b) const {
b->reset();
}
};
struct Particle : Base {
// ...
};
struct Pool {
std::unique_ptr<Particle, Deleter> create() {
// ...
}
}
int main() {
// ...
auto p1 = pool.create();
}
解决这个问题真的取决于
的组合
- 为什么需要池化对象?
- 为什么对象需要默认
lifeTime
为 100
?
- 为什么对象需要改变它们的
lifeTime
?
- 为什么从池中获取的现有对象需要将其
lifeTime
重置为 100
。
您已经部分回答了第一个问题,但我敢打赌您所说的提高性能的目标除了 "you think you need to improve performance" 之外没有其他任何依据。确实,这样的目标应该建立在测量性能不足的基础上,否则无异于过早的优化。
无论如何,如果我为了讨论而假设我上面的所有问题都有很好的答案,我会做以下事情;
class Particle
{
public:
// member functions that provide functions used by `main()`.
private: // note all the members below are private
Particle();
void reset()
{
lifeTime=100;
};
friend class Pool;
};
class Pool
{
public:
Particle *create()
{
Particle *p;
// obtain an object for p to point at
// that may mean release it from some "pool" or creating a new one
p->reset();
return p;
};
void give_back(Particle *&p)
{
// move the value of p back into whatever the "pool" is
p = NULL; // so the caller has some indication it should not use the object
};
};
int main()
{
// presumably pool is created somehow and visible here
auto* p1=pool.create();
// do things with p1
pool.give_back(p1); // we're done with p1
auto *p2 = pool.create();
// p2 might or might not point at what was previously p1
}
请注意,值 100
只出现在 reset()
函数中。
将构造函数设为私有和 Pool
的原因 friend
是为了防止意外创建新对象(即强制使用池)。
可选地,使 Particle::reset()
成为 public
允许 main()
调用 p1->reset()
,但这不是必需的。但是,从池中获取的所有对象(无论是新创建的还是重复使用的)都将被重置。
我可能还会使用 std::unique_ptr<Particle>
以便正确管理对象的生命周期,例如,如果您忘记将对象返回到池中。我将把实施这类事情作为练习。
要提高创建和销毁对象时的性能,可以使用池化。
在某些情况下,我不想深入研究自定义分配器或 char[]
等低级技术。
另一种方法是创建对象池.
但是,此技术不适用于 in-class 字段(内联)初始化。
起初,我根本不认为这是个问题。
但是,这个图案反复出现一百次,我想我应该有一些对策。
示例
假设我的程序的第一个版本是这样的:-
class Particle{
int lifeTime=100; //<-- inline initialization
//.... some function that modify "lifeTIme"
};
int main(){
auto p1=new Particle();
delete p1;
//... many particle created & deleted randomly ...
};
采用对象池后,我的程序可以是:-
class Particle{
int lifeTime=100; //<---- maintainability issue
void reset(){
lifeTime=100; //<---- maintainability issue
}
};
int main(){
auto* p1=pool.create();
//.... "Particle::reset()" have to be called somewhere.
};
重复代码会导致一些可维护性问题。
问题
如何在不牺牲代码可维护性和可读性的情况下将对象池应用于具有内联字段初始化的现有对象?
我目前的解决方法
我一般让构造函数调用reset()
.
class Particle{
int lifeTime;
public: Particle(){
reset(); //<---- call it here, or "pool" call it
}
void reset(){
lifeTime=100;
}
};
缺点:与旧的内联初始化相比,它降低了代码的可读性:-
int lifeTime=100;
对不起,如果这个问题太初学者了,我是C++的新手。
这是 std::unique_ptr<>
的常用用例:
class Base {
static constexpr int lifespan = 100;
int lifetime = lifespan;
public:
void reset() noexcept { lifetime = lifespan; }
}
struct Deleter {
void operator ()(Base* const b) const {
b->reset();
}
};
struct Particle : Base {
// ...
};
struct Pool {
std::unique_ptr<Particle, Deleter> create() {
// ...
}
}
int main() {
// ...
auto p1 = pool.create();
}
解决这个问题真的取决于
的组合- 为什么需要池化对象?
- 为什么对象需要默认
lifeTime
为100
? - 为什么对象需要改变它们的
lifeTime
? - 为什么从池中获取的现有对象需要将其
lifeTime
重置为100
。
您已经部分回答了第一个问题,但我敢打赌您所说的提高性能的目标除了 "you think you need to improve performance" 之外没有其他任何依据。确实,这样的目标应该建立在测量性能不足的基础上,否则无异于过早的优化。
无论如何,如果我为了讨论而假设我上面的所有问题都有很好的答案,我会做以下事情;
class Particle
{
public:
// member functions that provide functions used by `main()`.
private: // note all the members below are private
Particle();
void reset()
{
lifeTime=100;
};
friend class Pool;
};
class Pool
{
public:
Particle *create()
{
Particle *p;
// obtain an object for p to point at
// that may mean release it from some "pool" or creating a new one
p->reset();
return p;
};
void give_back(Particle *&p)
{
// move the value of p back into whatever the "pool" is
p = NULL; // so the caller has some indication it should not use the object
};
};
int main()
{
// presumably pool is created somehow and visible here
auto* p1=pool.create();
// do things with p1
pool.give_back(p1); // we're done with p1
auto *p2 = pool.create();
// p2 might or might not point at what was previously p1
}
请注意,值 100
只出现在 reset()
函数中。
将构造函数设为私有和 Pool
的原因 friend
是为了防止意外创建新对象(即强制使用池)。
可选地,使 Particle::reset()
成为 public
允许 main()
调用 p1->reset()
,但这不是必需的。但是,从池中获取的所有对象(无论是新创建的还是重复使用的)都将被重置。
我可能还会使用 std::unique_ptr<Particle>
以便正确管理对象的生命周期,例如,如果您忘记将对象返回到池中。我将把实施这类事情作为练习。