covariant return type and boost::shared_ptr

本文探讨了C++中协变返回类型的概念及其在智能指针应用中的挑战。通过实例说明了如何在继承体系中利用boost::shared_ptr,并提出了一种解决方案来克服因智能指针类型不协变带来的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

covariant return type and boost::shared_ptr

 

covariant return type翻译为协变返回值,意义就是在虚函数的返回值上,可以使用子类对象,而不必是基类对象.下面是一个例子:

struct Object

        {

        public:

                virtual ~Object()

                {}

        };

        struct Cloneable: public Object

        {

        public:

                virtual ~Cloneable()

                {}

                   virtual Object* Clone() const = 0;

        };

        struct Foo : public Cloneable

        {

                virtual Foo* Clone() const //注意这里

                {

                      return new Foo();

                }

        };

我们看到FooClone函数返回的是Foo对象,而不是Object对象.显然,C++的这个特点,对于设计继承类层次来说,在某些情况下使用非常方便,因为你不必被强迫使用dynamic_cast或者static_cast来进行类型转换了.

boost::shared_ptrboost智能指针中最为常用的一个,它是内存资源或者其他资源的正确释放的有力工具. boost::shared_ptr使用在函数返回值当中,也有很好的作用.例如上面的Clone函数的返回值是一个指针,那么这个指针的生命周期的维护就是一个问题.一般来说,这需要使用者和实现者之间有一个很好的协议,这个协议的严格遵守才能保证避免资源的泄露,这个问题也是C++世界中一个非常棘手的问题.引进boost::shared_ptr,可以在很大的程度上解决这个问题,代码如下:

struct Object

        {

        public:

                virtual ~Object()

                {}

        };

        typedef boost::shared_ptr<Object> ObjectPtr;

 

        struct Cloneable: public Object

        {

        public:

                virtual ~Cloneable()

                {}

                virtual ObjectPtr Clone() const = 0;

        };

        struct Foo;

        typedef boost::shared_ptr<Foo> FooPtr;

        struct Foo : public Cloneable

        {

                virtual FooPtr Clone() const

                {

                        return  boost::shared_ptr<Foo>(new Foo());

                }

        };

资源的释放由智能指针本身维护,用户不必再担心资源的泄露问题.这是一个非常好的方案.

但是,C++的世界不是如此的完美,上面的代码是不能编译成功的,原因是:虽然ObjectFoo之间存在继承关系,但是ObjectPtrFooPtr之间不存在继承关系,这意味着协变返回值在这里不起作用.

为了解决这个问题,boost的新闻组上http://lists.boost.org/boost-users/2003/02/2996.php 给出了如下的方案(注意这里是针对我们的例子修改以后的):

struct Object

        {

        public:

                virtual ~Object()

                {}

        };

        typedef boost::shared_ptr<Object> ObjectPtr;

 

        struct Cloneable: public Object

        {

        public:

                virtual ~Cloneable()

                {}

                virtual ObjectPtr DoClone() const = 0;

        };

        struct Foo;

        typedef boost::shared_ptr<Foo> FooPtr;

        struct Foo : public Cloneable

        {

                virtual ObjectPtr DoClone() const

                {

                        return  boost::shared_ptr<Foo>(new Foo());

                }

FooPtr Clone() const

                {

                        return  boost::dynamic_pointer_cast<X>(this-> DoClone ());

                }

           };

这个方案在一定程度上解决了问题,但是我个人非常不认同这个方案,主要的理由如下:

1.         Cloneable类型是一个接口类,一般来说,接口的抽象程度比较高,使用范围也很广.接口一般是用来抽象概念的,上面改变接口函数的做法,在一定的程度上伤害了这个概念.试想,如果你是Cloneable的最初设计者,你会把接口声明为DoClone?这种技术妨碍概念设计的做法,是应该竭力避免的.

2.         即便是从纯粹技术的角度说,这种做法也存在问题.上面的例子是两层继承设计,当然在实际中也可能是三层或者更多的层次,我们就以三层为例,为了使用这种技术,还得设定一个新的名称,难道是DoDoClone?!显然,这个的技术会随着层次的加深而越发的不可接受.所以说,这个技术的扩展性比较差.

其实,我认为更好的方法在boost中已经使用,这个方法是:

1)       上层的接口设计保持不变,仍然是从概念出发,例如Cloneable的接口函数名称仍然是Clone,返回值类型是ObjectPtr;

2)       下层的接口直接继承上层的接口,如果是实现类,那么并不使用协变返回值的技术,仍然是返回ObjectPtr;

3)       提供一个模板化的全局函数,实现向上转型.

上面例子的做法是:

struct Object

        {

        public:

                virtual ~Object()

                {}

        };

        typedef boost::shared_ptr<Object> ObjectPtr;

 

        struct Cloneable: public Object

        {

        public:

                virtual ~Cloneable()

                {}

                virtual ObjectPtr Clone() const = 0;

        };

        struct Foo;

        typedef boost::shared_ptr<Foo> FooPtr;

        struct Foo : public Cloneable

        {

                virtual ObjectPtr Clone() const

                {

                        return  ObjectPtr <Foo>(new Foo());

                }

        };

template<class X> shared_ptr<X> clone(X const & x)

{

    shared_ptr<X> px = boost::dynamic_pointer_cast<X>(x.Clone());

    assert(px);

    return px;

}

其实这里也可以使用boost:: static_pointer_cast.

 

/data/k50048780/cpp_mutator/mutators/BitStream.cpp: In member function ‘BitStream BitStream::slice(size_t, size_t) const’: /data/k50048780/cpp_mutator/mutators/BitStream.cpp:154:46: error: passing ‘const BitStream’ as ‘this’ argument discards qualifiers [-fpermissive] 154 | copy_bits(*this, start_bit, 0, bit_length); | ^ In file included from /data/k50048780/cpp_mutator/mutators/BitStream.cpp:5: /data/k50048780/cpp_mutator/mutators/BitStream.h:62:10: note: in call to ‘void BitStream::copy_bits(const BitStream&, size_t, size_t, size_t)’ 62 | void copy_bits(const BitStream& src, size_t src_start, size_t dest_start, size_t bit_count); | ^~~~~~~~~ CMakeFiles/mutator.dir/build.make:446: recipe for target 'CMakeFiles/mutator.dir/mutators/BitStream.cpp.o' failed make[3]: *** [CMakeFiles/mutator.dir/mutators/BitStream.cpp.o] Error 1 make[3]: *** Waiting for unfinished jobs.... /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp: In constructor ‘StringLengthEdgeCase::StringLengthEdgeCase(std::shared_ptr<StringBase>, std::mt19937)’: /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:10:37: error: no matching function for call to ‘IntegerEdgeCases::IntegerEdgeCases(std::shared_ptr<StringBase>&, std::mt19937&)’ 10 | : IntegerEdgeCases(obj, rand) { | ^ In file included from /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.h:10, from /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:5: /data/k50048780/cpp_mutator/mutators/string/../utils.h:146:5: note: candidate: ‘IntegerEdgeCases::IntegerEdgeCases(std::shared_ptr<NumberBase>, std::mt19937&)’ 146 | IntegerEdgeCases(std::shared_ptr<NumberBase> obj, std::mt19937 &rand); | ^~~~~~~~~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/../utils.h:146:50: note: no known conversion for argument 1 from ‘shared_ptr<StringBase>’ to ‘shared_ptr<NumberBase>’ 146 | IntegerEdgeCases(std::shared_ptr<NumberBase> obj, std::mt19937 &rand); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~ /data/k50048780/cpp_mutator/mutators/string/../utils.h:144:7: note: candidate: ‘IntegerEdgeCases::IntegerEdgeCases(const IntegerEdgeCases&)’ 144 | class IntegerEdgeCases : public Mutator { | ^~~~~~~~~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/../utils.h:144:7: note: candidate expects 1 argument, 2 provided /data/k50048780/cpp_mutator/mutators/string/../utils.h:144:7: note: candidate: ‘IntegerEdgeCases::IntegerEdgeCases(IntegerEdgeCases&&)’ /data/k50048780/cpp_mutator/mutators/string/../utils.h:144:7: note: candidate expects 1 argument, 2 provided /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp: In member function ‘virtual std::pair<long int, long int> StringLengthEdgeCase::get_limits(std::shared_ptr<StringBase>)’: /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:24:25: error: request for member ‘has_value’ in ‘((std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>*)(& obj))->std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>::operator->()->StringBase::<anonymous>.TypeBase<StringBase>::max_length’, which is of non-class type ‘int64_t’ {aka ‘long int’} 24 | if (obj->max_length.has_value()) { | ^~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:25:47: error: request for member ‘value’ in ‘((std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>*)(& obj))->std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>::operator->()->StringBase::<anonymous>.TypeBase<StringBase>::max_length’, which is of non-class type ‘int64_t’ {aka ‘long int’} 25 | max_ = std::min(max_, obj->max_length.value()); | ^~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:29:25: error: request for member ‘has_value’ in ‘((std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>*)(& obj))->std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>::operator->()->StringBase::<anonymous>.TypeBase<StringBase>::min_length’, which is of non-class type ‘int64_t’ {aka ‘long int’} 29 | if (obj->min_length.has_value()) { | ^~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:30:47: error: request for member ‘value’ in ‘((std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>*)(& obj))->std::__shared_ptr_access<StringBase, __gnu_cxx::_S_atomic, false, false>::operator->()->StringBase::<anonymous>.TypeBase<StringBase>::min_length’, which is of non-class type ‘int64_t’ {aka ‘long int’} 30 | min_ = std::max(min_, obj->min_length.value()); | ^~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp: At global scope: /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:42:6: error: no declaration matches ‘void StringLengthEdgeCase::perform_mutation(std::shared_ptr<StringBase>, int64_t)’ 42 | void StringLengthEdgeCase::perform_mutation(std::shared_ptr<StringBase> obj, int64_t value) { | ^~~~~~~~~~~~~~~~~~~~ In file included from /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.cpp:5: /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.h:16:10: note: candidate is: ‘void StringLengthEdgeCase::perform_mutation(std::shared_ptr<String>, size_t)’ 16 | void perform_mutation(std::shared_ptr<String> obj, size_t index); | ^~~~~~~~~~~~~~~~ /data/k50048780/cpp_mutator/mutators/string/StringLengthEdgeCase.h:13:7: note: ‘class StringLengthEdgeCase’ defined here 13 | class StringLengthEdgeCase : public IntegerEdgeCases { | ^~~~~~~~~~~~~~~~~~~~ In file included from /data/k50048780/cpp_mutator/mutators/MutatorFactory.cpp:8: /data/k50048780/cpp_mutator/mutators/number/NumberVariance.h:13:32: error: invalid covariant return type for ‘virtual std::pair<long unsigned int, long unsigned int> NumberVariance::get_limits(std::shared_ptr<NumberBase>)’ 13 | std::pair<size_t ,size_t > get_limits(std::shared_ptr<NumberBase> obj); | ^~~~~~~~~~ In file included from /data/k50048780/cpp_mutator/mutators/string/../../utils/sample_util.h:12, from /data/k50048780/cpp_mutator/mutators/string/StringCaseLower.h:8, from /data/k50048780/cpp_mutator/mutators/MutatorFactory.cpp:6: /data/k50048780/cpp_mutator/mutators/string/../../utils/../mutators/utils.h:70:51: note: overridden function is ‘virtual std::pair<long int, long int> IntegerVariance::get_limits(std::shared_ptr<NumberBase>)’ 70 | virtual std::pair<std::int64_t, std::int64_t> get_limits(std::shared_ptr<NumberBase> obj); | ^~~~~~~~~~
最新发布
06-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值