【问题标题】:StackPointer template堆栈指针模板
【发布时间】:2013-03-07 23:25:51
【问题描述】:

我正在阅读“现代 C++ 设计”并想到了构建一个类的想法,该类的作用类似于指针,但它会在堆栈上而不是在堆上分配对象。它可以用于通常会返回指向在堆上分配的对象的指针的函数。

我会在粘贴代码之前问我的问题:

  1. 已经有类似的东西了吗?
  2. 有机会使用吗? (当然,如果实施得更准确)
  3. 为什么使用boost::mpl::max_element(已注释掉)的版本不起作用?
  4. 如果没有参数,如何调用模板化构造函数? (我的意思是:template <class U> StackPointer() { ... })?

代码如下:

#include <iostream>

#include <boost/mpl/vector.hpp>
#include <boost/mpl/max_element.hpp>
#include <boost/mpl/empty.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/front.hpp>

template <class V, size_t VS=boost::mpl::size<V>::type::value>
struct max_size
{
    typedef typename boost::mpl::pop_front<V>::type subvector;
    typedef typename boost::mpl::front<V>::type front_type;
    static size_t const value = sizeof(front_type) > max_size<subvector>::value ?
                sizeof(front_type) : max_size<subvector>::value;
};

template <class V>
struct max_size<V, 0>
{
    static size_t const value = 0;
};

class StackPointerImplBase
{
public:
    virtual void clone(char const* from, char* to) const = 0;
};

template <class T>
class StackPointerImpl : public StackPointerImplBase
{
public:
    virtual void clone(char const* from, char *to) const
    {
        new(to) T(*reinterpret_cast<T const*>(from));
    }
};

template <class Base, class DerivedTypes>
class StackPointer
{
public:
    template <class T>
    StackPointer(T const& t)
    {
        std::cout << "Size of m_buf: "  << sizeof(m_buf) << std::endl;
        new(m_impl_buf) StackPointerImpl<T>();
        new(m_buf) T(t);
    }

    StackPointer(StackPointer const& sp)
    {
        //TODO: COPY m_impl_buf
        reinterpret_cast<StackPointerImplBase const*>(sp.m_impl_buf)->clone(sp.m_buf, m_buf);
    }

public:
    ~StackPointer()
    {
        get_pointer()->~Base();
    }

    Base* operator->()
    {
        return get_pointer();
    }

private:
    Base* get_pointer()
    {
        return reinterpret_cast<Base*>(m_buf);
    }

private:
    //typedef max_size<DerivedTypes> xxx_type;
    //typedef typename boost::mpl::max_element<DerivedTypes>::type::type biggest_type;
    //char m_buf[sizeof(typename boost::mpl::max_element<DerivedTypes>::type::type)];
    char m_buf[max_size<DerivedTypes>::value];
    char m_impl_buf[sizeof(StackPointerImplBase)];
};

class Shape
{
public:
    virtual ~Shape() {}

    virtual void say() const { std::cout << "I'm a shape" << std::endl; }
};

class Circle : public Shape
{
public:
    virtual void say() const { std::cout << "I'm a circle" << std::endl; }

private:
    float m_x;
    float m_y;
    float m_r;
};

class Line : public Shape
{
public:
    virtual void say() const { std::cout << "I'm a Line" << std::endl; }

private:
    float m_x1;
    float m_y1;
    float m_x2;
    float m_y2;
};


typedef StackPointer<Shape, boost::mpl::vector<Circle, Line> > ShapeStackPtr;

ShapeStackPtr read_shape()
{
    Line c;
    return ShapeStackPtr(c);
}


int main(int argc, char *argv[])
{

    {
        ShapeStackPtr shape = read_shape();
        shape->say();
    }

    return 0;
}

【问题讨论】:

  • 为什么不想使用堆?
  • 在我看来,您的方法的一个主要问题是必须事先知道所有派生类。如果我想向Shape 添加一个新的子类怎么办?我需要更改ShapeStackPtr 的所有用途,或者创建一个新类型的StackPointer。如果可以使指针仅与基类一起使用,这可能会很有趣,但我怀疑这是可能的。另一方面,我认为分配性能问题可能最好通过使用自定义分配器或覆盖 new/delete 来处理。

标签: c++ templates metaprogramming template-meta-programming boost-mpl


【解决方案1】:

Boost optional 和你描述的很相似。

它使用指针语法,因此它可以模拟指针语言中的 NULL 指针。

【讨论】:

  • 如果我理解正确, boost optional 允许您返回“NULL”或一种类型。我的类试图返回一个可以处理从 Base 类型继承的许多类型的对象。
  • boost::any 可能是你想要的
【解决方案2】:

你不能在堆栈上分配一个对象,然后返回任何指向它的东西。从函数返回后,堆栈帧消失了,指向该内存无效。

当您在函数中声明局部变量时,它们被分配到堆栈上,然后在函数返回之前被销毁,堆栈框架被废弃。

因此,您可以为堆上的对象分配内存并通过指针引用它们。或者在堆栈上的函数中有局部变量。

或者你的意思是作为一种处理被调用函数返回值的方法?但是,无论如何您都需要将数据从堆复制到堆栈,所以如果您需要/想要堆栈上的副本,为什么不直接使用T obj = *foo()

对我来说,这听起来像是您试图使编译器已经有效处理的事情复杂化。

【讨论】:

  • 在我的例子中我不能做 T obj = *foo() 因为我不知道返回的类型。我只知道基类。通常, foo 将返回一个指向基类的指针,该指针指向在堆上分配的继承类型之一。你不能在他的堆栈上做类似的分配。在第二种情况下,您唯一的选择是按值返回。
  • 所以如果一个函数返回一个指向堆分配对象的指针。然后呢?我仍然没有遵循您想要在这里完成的任务。你到底想在堆栈上存储什么?
  • 我想避免这种方式的堆分配。作为更便宜的选择。
  • 那么返回指针的函数在哪里为它返回的对象分配了内存?
  • 我看不出这会带来什么好处。当您使用 StackPointer 从函数返回时,会将 mbuf 中的数据复制到新的 StackPointer。这与复制值相同。如果你在堆上分配你的对象,你只需要传递一个指向它的指针,这通常要快得多。因此,不要先在堆上分配一次对象。在被调用函数堆栈上创建一个 char 缓冲区,然后将所有数据从调用堆栈帧复制,并且很可能复制比对象大小更多的字节。正如我所说,您正在尝试重新发明轮子。
猜你喜欢
  • 2013-01-20
  • 2015-11-16
  • 1970-01-01
  • 1970-01-01
  • 2014-10-16
  • 2021-09-17
  • 1970-01-01
  • 2015-08-05
  • 2017-06-06
相关资源
最近更新 更多