从公共基 class 继承的对象的条件声明以传递对其中之一的引用
Conditional declaration of objects inherting from a common base class to pass a reference to one of them
假设我有两个 类 继承自一个共同的基础,例如
class Thing{
public:
virtual void f()=0;
};
class Thing_variant_a: public Thing{
public:
void f(){
std::cout<<"I am (a)"<<std::endl;
}
};
class Thing_variant_b: public Thing{
public:
void f(){
std::cout<<"I am (b)"<<std::endl;
}
};
还有一个函数将对 Thing
对象的引用作为参数。
void function(Thing& t){
t.f();
}
根据条件,我想用 thing_a
或 thing_b
调用 function
(并可能在某些时候扩展它,添加另一种可能性 thing_c
)
我知道我可以使用指针来做到这一点
Thing *t = nullptr;
if(condition_a){
t = new Thing_variant_a();
} else if(condition_b){
t = new Thing_variant_b();
}
function(*t);
不过,我想知道有没有更好的办法,那就是
不分配堆内存
不需要我在某些时候删除t
(可能是智能指针,但我不太了解那些)
确保我总是传递一个有效的 Thing
函数引用(复杂结构中的条件可能比这个最小的例子更多)我可以做 if(t){ function(*t);}else{/*handle error*/}
),但似乎应该有一个更优雅的解决方案。
如果不是以上所有都可行,可以任意组合吗?
这是一种不使用堆或指针的方法:
Thing_variant_a thingA;
Thing_variant_b thingB;
if(condition_a){
function(thingA);
} else if(condition_b){
function(thingB);
}
如果需要,可以通过三元运算符将其减少为单个调用:
Thing_variant_a thingA;
Thing_variant_b thingB;
function(condition_a ? static_cast<Thing &>(thingA) : static_cast<Thing &>(thingB));
就引用而言,C++ 中的引用必须始终为非 NULL——因此如果您尝试取消引用 NULL 指针(例如,在 t==NULL
时调用 function(*t)
)您已经调用了未定义的行为并且注定要失败; function()
中的代码无法拯救您。因此,如果您的指针有任何变化为 NULL,则必须在 取消引用之前检查 。
我会尽量回答你的每一个问题
does not allocate heap memory
不幸的是,c++ 只支持使用指针的多态性。我猜你在这里面临的问题是内存碎片(这意味着你的指针在堆中无处不在)。处理该问题的最佳方法是使用内存池分配内存。
您可以使用 std::variant
,但您仍需要测试变体中当前可用的类型。
does not require me to take care of deleting t at some point (probably smart pointers, but I don't know much about those)
您可以使用 std::unique_ptr
,当没有人持有该指针时,它基本上会调用析构函数。
ensures I always pass a valid Thing reference to function (there might be more conditionals in a complicated structure than in this minimal example) I could do if(t){ function(*t);}else{/handle error/}), but it seems like there should be a more elegant solution.
如果您使用指针,您可以像现在一样检查 nullptr
。我不确定你所说的有效引用是什么意思,因为引用总是指向某物并且不能为空。
这听起来很像 XY 问题。您的问题可能有完全不同的解决方案。
C++ 是一种静态类型的语言;这意味着给定代码路径中使用的类型在编译时是固定的。动态类型(在 运行 时间已知的类型)通常通过堆或一次性分配,然后在 运行 时间选择。
正如您所注意到的那样,您的情况不太可能..
例如,您可以只有两个不同的代码路径:
if (condition_a) {
Thing_variant_a a;
function(a);
} else if (condition_b) {
Thing_variant_a b;
function(b);
}
预分配类型:
Thing_variant_a a;
Thing_variant_a b;
if (condition_a) {
function(a);
} else if (condition_b) {
function(b);
}
或使用模板:
template<typename T>
void do_something() {
T t;
function(t);
}
// somewhere else in the code ...
do_something<Thing_variant_a>();
// or ...
do_something<Thing_variant_b>();
这里有一个使用动态内存的方法 unique_ptr
:
std::unique_ptr<Thing> t;
if (condition_a) {
t = std::make_unique<Thing_variant_a>();
} else if (condition_b) {
t = std::make_unique<Thing_variant_b>();
}
function(*t);
// t is delete'd automatically at end of scope...
顺便说一句,像 int f(){...}
这样的函数应该 return 一些 int
值。
假设我有两个 类 继承自一个共同的基础,例如
class Thing{
public:
virtual void f()=0;
};
class Thing_variant_a: public Thing{
public:
void f(){
std::cout<<"I am (a)"<<std::endl;
}
};
class Thing_variant_b: public Thing{
public:
void f(){
std::cout<<"I am (b)"<<std::endl;
}
};
还有一个函数将对 Thing
对象的引用作为参数。
void function(Thing& t){
t.f();
}
根据条件,我想用 thing_a
或 thing_b
调用 function
(并可能在某些时候扩展它,添加另一种可能性 thing_c
)
我知道我可以使用指针来做到这一点
Thing *t = nullptr;
if(condition_a){
t = new Thing_variant_a();
} else if(condition_b){
t = new Thing_variant_b();
}
function(*t);
不过,我想知道有没有更好的办法,那就是
不分配堆内存
不需要我在某些时候删除
t
(可能是智能指针,但我不太了解那些)确保我总是传递一个有效的
Thing
函数引用(复杂结构中的条件可能比这个最小的例子更多)我可以做if(t){ function(*t);}else{/*handle error*/}
),但似乎应该有一个更优雅的解决方案。
如果不是以上所有都可行,可以任意组合吗?
这是一种不使用堆或指针的方法:
Thing_variant_a thingA;
Thing_variant_b thingB;
if(condition_a){
function(thingA);
} else if(condition_b){
function(thingB);
}
如果需要,可以通过三元运算符将其减少为单个调用:
Thing_variant_a thingA;
Thing_variant_b thingB;
function(condition_a ? static_cast<Thing &>(thingA) : static_cast<Thing &>(thingB));
就引用而言,C++ 中的引用必须始终为非 NULL——因此如果您尝试取消引用 NULL 指针(例如,在 t==NULL
时调用 function(*t)
)您已经调用了未定义的行为并且注定要失败; function()
中的代码无法拯救您。因此,如果您的指针有任何变化为 NULL,则必须在 取消引用之前检查 。
我会尽量回答你的每一个问题
does not allocate heap memory
不幸的是,c++ 只支持使用指针的多态性。我猜你在这里面临的问题是内存碎片(这意味着你的指针在堆中无处不在)。处理该问题的最佳方法是使用内存池分配内存。
您可以使用 std::variant
,但您仍需要测试变体中当前可用的类型。
does not require me to take care of deleting t at some point (probably smart pointers, but I don't know much about those)
您可以使用 std::unique_ptr
,当没有人持有该指针时,它基本上会调用析构函数。
ensures I always pass a valid Thing reference to function (there might be more conditionals in a complicated structure than in this minimal example) I could do if(t){ function(*t);}else{/handle error/}), but it seems like there should be a more elegant solution.
如果您使用指针,您可以像现在一样检查 nullptr
。我不确定你所说的有效引用是什么意思,因为引用总是指向某物并且不能为空。
这听起来很像 XY 问题。您的问题可能有完全不同的解决方案。
C++ 是一种静态类型的语言;这意味着给定代码路径中使用的类型在编译时是固定的。动态类型(在 运行 时间已知的类型)通常通过堆或一次性分配,然后在 运行 时间选择。
正如您所注意到的那样,您的情况不太可能..
例如,您可以只有两个不同的代码路径:
if (condition_a) {
Thing_variant_a a;
function(a);
} else if (condition_b) {
Thing_variant_a b;
function(b);
}
预分配类型:
Thing_variant_a a;
Thing_variant_a b;
if (condition_a) {
function(a);
} else if (condition_b) {
function(b);
}
或使用模板:
template<typename T>
void do_something() {
T t;
function(t);
}
// somewhere else in the code ...
do_something<Thing_variant_a>();
// or ...
do_something<Thing_variant_b>();
这里有一个使用动态内存的方法 unique_ptr
:
std::unique_ptr<Thing> t;
if (condition_a) {
t = std::make_unique<Thing_variant_a>();
} else if (condition_b) {
t = std::make_unique<Thing_variant_b>();
}
function(*t);
// t is delete'd automatically at end of scope...
顺便说一句,像 int f(){...}
这样的函数应该 return 一些 int
值。