共享指针本质上不强制转换为基/派生类型
Shared pointers do not intrinsically cast to base/derived types
我所做的:我最近开始编写一个多线程的生产者-消费者风格的队列。最初,我使用智能指针,但最终将它们全部更改为原始指针,并手动管理它们的生命周期和内存管理(如果感兴趣,请在最后编写代码)。
我要找的是:支持或反对这个猜想的论据:
继承不能和共享指针共处一室,就像原始指针和引用对象一样。
我的推理:Base
和Derived
对象是协变的。原始指针(Base*
和Derived*
)也是如此。共享指针(shared_ptr<Base>
和shared_ptr<Derived>
)不是。
程序员必须使用dynamic_pointer_cast
进行许多概念上不必要的向下转换,这会使代码变得丑陋,并且在编译时和运行时都有些昂贵。
这让我想知道在面向对象的设计中是否应该避免共享指针,因为它们的好处并没有超过它们的成本和令人头痛的问题。
修改前的代码(为了可读性,省略了多线程):
typedef shared_ptr<Animal> animal_ptr;
typedef shared_ptr<Dog> dog_ptr;
class Buffer {
private:
mutex mu_;
condition_variable cond_;
deque<shared_ptr<Animal> > buffer_;
public:
void add(shared_ptr<Animal> req) {
std::unique_lock<std::mutex> locker(mu_);
cond_.wait(locker, [this](){return buffer_.size() < size_;});
buffer_.push_back(req);
locker.unlock();
cond_.notify_all();
}
shared_ptr<Animal> remove() {
unique_lock<mutex> locker(mu_);
cond_.wait(locker, [this](){return buffer_.size() > 0;});
shared_ptr<Animal> back = buffer_.back();
buffer_.pop_back();
locker.unlock();
cond_.notify_all();
}
};
int main() {
Buffer buffer;
animal_ptr bPtr1 (new Animal()); // buffer.add() works just fine
dog_ptr dPtr1 (new Dog()); // EDIT: works fine too.
animal_ptr dptr2 (new Dog()); // EDIT: it's okay
...
buffer.remove(); // returns a base class object, requires downcasting to access derived members
}
EDIT以获得更多说明,以及为什么共享指针与原始指针处理继承的方式不同:
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.
不,它工作。
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);
}
最后两个"A"打印正确。你的函数不工作的原因是因为Dog* != std::shared_ptr<Dog>
.
std::shared_ptr<derived>
隐式地转换为std::shared_ptr<base>
,因此将派生指针传递给接受基指针的函数应该没有错误,除非您使用的编译器不好。
一个编译良好的函数示例:
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!
现在我明白你遇到的问题了。如果始终在任何地方使用shared_ptr,则可以像传递原始指针一样传递Base和Derived的shared_ptr对象。我把你举的例子具体化了。
当您混合使用shared_ptr和原始指针时,问题就出现了。第一个问题与重载有关,其中shared_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;
}
- 在 C++ 中用派生类型重写成员函数
- 在 C++ 中将对象转换为派生类型
- C++如果采用类类型的函数被传递派生类型,有没有办法给出错误?
- 返回派生类型时出现协变类型错误
- 如何模板化堆栈分配的多态指针数组到接口,包括派生类型的相应点?
- 基于派生类型的编译时行为分支
- 按类型排序向量并按类型或派生类型搜索
- 为什么"运算符<<"不适用于指向派生类型的成员?
- 无法返回派生类型的标准::unique_ptr<>
- 无法从派生类型的作用域访问另一个实例的受保护成员
- dynamic_cast到具有未知模板参数的派生类型
- 派生类型的成员
- 视觉C++ dynamic_cast返回 NULL,尽管对象是派生类型
- 在运算符中动态强制转换为派生类型<<
- 如何在不转换回基类类型的情况下返回派生类型的对象?
- 将派生指针分配给C 中的其他派生类型
- 如何将派生类型的对象放在用于基本类型的向量中
- C++ - 无法将派生类型作为基类型列表传递给构造函数
- 如何在基本类型参考中存储对派生类型的参考
- 统一处理具有协变类型的函数指针(如何使用派生类型调用回调?