C++ 共享指针本质上并不强制转换为基/派生类型
我所做的:我最近开始编写一个多线程生产者-消费者风格的队列。起初,我使用智能指针,但最终将它们全部更改为原始指针,并手动管理它们的生命周期和内存管理(如果有兴趣,请在最后编写代码) 我在寻找什么:支持或反对这个猜想的论据: 继承不能像对待原始指针和引用对象那样,与共享指针放在同一个房间里 我的推理:C++ 共享指针本质上并不强制转换为基/派生类型,c++,c++11,inheritance,casting,shared-ptr,C++,C++11,Inheritance,Casting,Shared Ptr,我所做的:我最近开始编写一个多线程生产者-消费者风格的队列。起初,我使用智能指针,但最终将它们全部更改为原始指针,并手动管理它们的生命周期和内存管理(如果有兴趣,请在最后编写代码) 我在寻找什么:支持或反对这个猜想的论据: 继承不能像对待原始指针和引用对象那样,与共享指针放在同一个房间里 我的推理: Base和派生的对象是协变的。原始指针(Base*和Derived*)也是如此。共享指针(Shared_ptr和Shared_ptr)不可用 程序员必须使用dynamic\u pointer\u c
Base
和派生的
对象是协变的。原始指针(Base*
和Derived*
)也是如此。共享指针(Shared_ptr
和Shared_ptr
)不可用
程序员必须使用dynamic\u pointer\u cast
进行许多概念上不必要的向下转换,这使得代码很难看,并且在编译时和运行时都有点昂贵
这让我想知道,在面向对象的设计中是否应该避免使用共享指针,因为它们的好处并不超过它们的花费和头痛
更改前我的代码(为了可读性,省略了多线程):
typedef共享\u ptr动物\u ptr;
typedef共享狗;
类缓冲区{
私人:
互斥;
条件变量条件;
德克缓冲区;
公众:
无效添加(共享请求){
标准:独特的锁柜(mu);
cond.wait(locker,[this](){返回缓冲区大小()0;});
shared_ptr back=缓冲区_u.back();
buffer_u.pop_back();
locker.unlock();
条件通知所有人();
}
};
int main(){
缓冲区;
animal_ptr bPtr1(new animal());//buffer.add()工作正常
dog_ptr dPtr1(new dog());//编辑:也可以。
动物_ptrdptr2(新狗());//编辑:没关系
...
buffer.remove();//返回基类对象,需要向下转换才能访问派生成员
}
编辑了解更多信息,以及为什么共享指针对待继承的方式不同于原始指针:
void func1(shared_ptr<Animal> ptr);
void func2(Animal* ptr);
...
Dog* rawPtr = new Dog();
func1(dPtr1); // is not possible, requires upcasting
func2(rawPtr); // is ok.
void func1(共享函数);
无效功能2(动物*ptr);
...
狗*rawPtr=新狗();
功能1(dPtr1);//不可能,需要向上投射
func2(rawPtr);//可以。
现在我了解了您面临的问题。如果您在任何地方都始终使用shared_ptr,那么就能够传递与原始指针相同的Base和派生的shared_ptr对象。我充实了你举的例子
当您混合使用共享指针和原始指针时,问题就出现了。第一个问题与重载有关,其中共享的_ptr函数将转发给执行实际工作的原始指针的函数。当使用从共享指针获得的底层原始指针进行调用时,第二个问题就消失了
class Animal
{
public:
};
class Dog : public Animal
{
public:
};
using std::shared_ptr;
typedef shared_ptr<Animal> animal_ptr;
typedef shared_ptr<Dog> dog_ptr;
class Buffer {
private:
std::deque<shared_ptr<Animal> > buffer_;
public:
void add(shared_ptr<Animal> req) {
buffer_.push_back(req);
}
shared_ptr<Animal> remove() {
shared_ptr<Animal> back = buffer_.back();
buffer_.pop_back();
}
};
void func1(Animal* ptr)
{}
void func1(shared_ptr<Animal> ptr)
{
func1(ptr.get());
}
void func2(Animal* ptr)
{}
int main()
{
Buffer buffer;
animal_ptr bPtr1(new Animal()); // buffer.add() works just fine
dog_ptr dPtr1(new Dog()); // requires upcasting before buffer.add()
animal_ptr dptr2(new Dog()); // returns error, as they are covariant
Dog* rawPtr = new Dog();
func2(rawPtr); // is ok.
func1(dPtr1); // is ok.
func1(rawPtr); // requires overloading func1 with shared_ptr and raw pointer signatures
func2(dPtr1.get()); // is okay when using underlying raw ptr
return 0;
}
类动物
{
公众:
};
犬类:公共动物
{
公众:
};
使用std::shared_ptr;
typedef共享动物;
typedef共享狗;
类缓冲区{
私人:
标准::德克缓冲区;
公众:
无效添加(共享请求){
缓冲区推回(req);
}
共享\u ptr remove(){
shared_ptr back=缓冲区_u.back();
buffer_u.pop_back();
}
};
无效功能1(动物*ptr)
{}
无效功能1(共享\u ptr ptr)
{
func1(ptr.get());
}
无效功能2(动物*ptr)
{}
int main()
{
缓冲区;
animal_ptr bPtr1(new animal());//buffer.add()工作正常
dog_ptr dPtr1(new dog());//需要在buffer.add()之前向上转换
animal_ptr dptr2(new Dog());//返回错误,因为它们是协变的
狗*rawPtr=新狗();
func2(rawPtr);//正常。
func1(dPtr1);//正常。
func1(rawPtr);//需要使用共享的\u ptr和原始指针签名重载func1
func2(dPtr1.get());//在使用底层原始ptr时可以
返回0;
}
不,它可以工作。
http://coliru.stacked-crooked.com/a/b2a83c740ed60521
#包括
#包括
结构A{
void print(){std::cout A::print();
//这管用!
打印(ptr_a);
打印(ptr_b);
}
最后两个“A”正确打印。函数无法工作的原因是因为Dog*!=std::shared_ptr
std::shared_ptr
可以隐式转换为std::shared_ptr
,因此除非使用了错误的编译器,否则在将派生指针传递给使用基指针的函数时应该不会出错
编译精细的示例函数:
class animal{
public:
virtual auto print() const -> void = 0;
};
class dog: public animal{
public:
auto print() const -> void{ std::cout << "I'm a Dog!\n"; }
};
class cat: public animal{
public:
auto print() const -> void{ std::cout << "I'm not a Dog!\n"; }
};
auto func(std::shared_ptr<animal> ptr){
ptr->print();
}
auto main() -> int{
auto dog_ptr = std::make_shared<dog>();
auto cat_ptr = std::make_shared<cat>();
func(dog_ptr);
func(cat_ptr);
}
您所说的
返回错误的确切含义是什么?我的第二个问题是在buffer.add()之前需要向上转换是什么意思
?请参阅我前面的代码示例。@AndyG您的第二条评论也是有效的。我的错误是,我在简单的代码中忘记了从类动物
继承,因此产生了所有的误解。我仍然不理解向上和向下转换的必要性…@VermillionAzure我解释过:调用buffer.remove()为您提供一个共享的\u ptr,它不能自动强制转换为shahred\u ptr,并且需要使用动态\u指针进行运行时检查\u castNo代码中没有错误。正如我所解释的,它会导致不必要的向下转换。例如,假设您有Dog::print(),这是特定于狗的,而动物没有。您必须动态强制转换从缓冲区获得的结果共享指针才能访问该方法。@narengi正则指针也是如此……是的,正如VermillionAzure所说,正则指针也是如此。我在使用共享\u ptr insta时没有遇到任何问题nces来替换原始指针。您能提供一个共享\u ptr行为不同的示例吗?@VermillionAzure如果有意义,请查看编辑。@narengi-Goo
http://coliru.stacked-crooked.com/a/b2a83c740ed60521
#include <iostream>
#include <memory>
struct A {
void print() { std::cout << "A" << std::endl; }
};
struct B : public A {
void print() { std::cout << "B" << std::endl; }
};
void print(std::shared_ptr<A> a) {
a->print();
}
int main() {
std::shared_ptr<A> ptr_a(new A);
std::shared_ptr<B> ptr_b(new B);
ptr_a->print();
ptr_b->print();
ptr_b->A::print();
//THIS WORKS!
print(ptr_a);
print(ptr_b);
}
class animal{
public:
virtual auto print() const -> void = 0;
};
class dog: public animal{
public:
auto print() const -> void{ std::cout << "I'm a Dog!\n"; }
};
class cat: public animal{
public:
auto print() const -> void{ std::cout << "I'm not a Dog!\n"; }
};
auto func(std::shared_ptr<animal> ptr){
ptr->print();
}
auto main() -> int{
auto dog_ptr = std::make_shared<dog>();
auto cat_ptr = std::make_shared<cat>();
func(dog_ptr);
func(cat_ptr);
}
I'm a Dog!
I'm not a Dog!