C++ 有没有一种方法可以在不使用new(出于性能原因)的情况下从函数返回抽象

C++ 有没有一种方法可以在不使用new(出于性能原因)的情况下从函数返回抽象,c++,oop,C++,Oop,例如,我有一个函数pet\u maker(),它创建并返回一个Cat或Dog作为基本pet。我想多次调用此函数,并对返回的Pet执行一些操作 传统上,我会在pet_maker()中new猫或Dog并返回指向它的指针,但是new调用要比在堆栈上执行所有操作慢得多 是否有一种简洁的方法可以让任何人都可以将其作为抽象返回,而不必在每次调用函数时都进行新的操作,或者是否有其他方法可以让我快速创建并返回抽象?如果您需要多态性,使用new几乎是不可避免的。但新功能运行缓慢的原因是它每次都在寻找空闲内存。您可

例如,我有一个函数
pet\u maker()
,它创建并返回一个
Cat
Dog
作为基本
pet
。我想多次调用此函数,并对返回的
Pet
执行一些操作

传统上,我会在
pet_maker()
new
Dog
并返回指向它的指针,但是
new
调用要比在堆栈上执行所有操作慢得多


是否有一种简洁的方法可以让任何人都可以将其作为抽象返回,而不必在每次调用函数时都进行新的操作,或者是否有其他方法可以让我快速创建并返回抽象?

如果您需要多态性,使用new几乎是不可避免的。但新功能运行缓慢的原因是它每次都在寻找空闲内存。您可以做的是编写自己的运算符new,理论上,它可以使用预先分配的内存块,速度非常快


涵盖您可能需要的许多方面。

这取决于您拥有的确切用例,以及您愿意容忍的限制。例如,如果您同意重复使用相同的对象,而不是每次都有新的副本,则可以在函数中返回对静态对象的引用:

Pet& pet_maker()
{
static Dog dog;
static Cat cat;

    //...

    if(shouldReturnDog) {
        //manipulate dog as necessary
        //...
        return dog;
    }
    else
    {
        //manipulate cat as necessary
        //...
        return cat;
    }
}
如果客户机代码接受它不拥有返回的对象并且重用了相同的物理实例,那么这种方法就可以工作


如果这组假设不合适,还有其他一些技巧。

您可以创建一个堆栈分配器实例(当然有一些最大限制),并将其作为参数传递给您的
pet\u maker
函数。然后,在堆栈分配器提供的地址上执行
placement new
,而不是常规的
new


如果超过堆栈分配器的
max\u size
,您可能还可以默认为
new

在某个时候,有人将不得不分配内存并初始化对象。如果按需执行这些操作,那么通过
new
使用堆花费的时间太长,那么为什么不在池中预先分配一些then呢。然后可以根据需要初始化每个单独的对象。不利的一面是,你可能会有一堆额外的物体在周围放置一段时间

如果实际初始化对象是问题,而不是内存分配,那么您可以考虑保持一个预生成的对象,并使用该模式进行更快的初始化。p>


为了获得最佳结果,内存分配是一个问题,初始化时间也是一个问题,您可以将这两种策略结合起来。

一种方法是通过分析提前计算出您的程序需要多少种类型的对象

然后,您可以提前分配适当大小的数组,只要您有簿记来跟踪分配

比如,

#include <array>

//   Ncats, Ndogs, etc are predefined constants specifying the number of cats and dogs

std::array<Cat, Ncats> cats;
std::array<Dog, Ndogs> dogs;

//  bookkeeping - track the returned number of cats and dogs

std::size_t Rcats = 0, Rdogs = 0;

Pet *pet_maker()
{
    // determine what needs to be returned

    if (return_cat)
    {
       assert(Rcats < Ncats);
       return &cats[Rcats++];
    }
    else if (return_dog)
    {
       assert(Rdogs < Ndogs);
       return &dogs[Rdogs++];
    }
    else
    {
        // handle other case somehow
    }
}
#包括
//NCAT、NDOG等是预定义的常量,用于指定猫和狗的数量
性病:阵列猫;
阵列狗;
//簿记-跟踪返回的猫和狗的数量
std::size_t Rcats=0,Rdogs=0;
宠物*宠物制造者()
{
//确定需要返回的内容
如果(返回_cat)
{
断言(RCAT
当然,最大的折衷是需要提前明确确定每种动物的数量,并分别跟踪每种动物


然而,如果您希望避免动态内存分配(operator
new
),那么这种方式——看起来很严厉——提供了绝对的保证。使用operator
new
可以显式地允许在运行时确定所需对象的数量。相反,为了避免使用operator
new
,但允许某些功能安全地访问多个对象,有必要预先确定对象的数量。

每次分配都是一项开销,因此您可以通过分配整个对象数组而不是一次分配一个对象来获得好处

您可以使用以下方法来实现此目标:

class Pet { public: virtual ~Pet() {} virtual std::string talk() const = 0; };
class Cat: public Pet { std::string talk() const override { return "meow"; }};
class Dog: public Pet { std::string talk() const override { return "woof"; }};
class Pig: public Pet { std::string talk() const override { return "oink"; }};

class PetMaker
{
    // std::deque never re-allocates when adding
    // elements which is important when distributing
    // pointers to the elements
    std::deque<Cat> cats;
    std::deque<Dog> dogs;
    std::deque<Pig> pigs;

public:

    Pet* make()
    {
        switch(std::rand() % 3)
        {
            case 0:
                cats.emplace_back();
                return &cats.back();
            case 1:
                dogs.emplace_back();
                return &dogs.back();
        }
        pigs.emplace_back();
        return &pigs.back();
    }
};

int main()
{
    std::srand(std::time(0));

    PetMaker maker;

    std::vector<Pet*> pets;

    for(auto i = 0; i < 100; ++i)
        pets.push_back(maker.make());

    for(auto pet: pets)
        std::cout << pet->talk() << '\n';
}
classpet{public:virtual~Pet(){}virtual std::string talk()const=0;};
类Cat:publicpet{std::string talk()const override{return“meow”;};
类狗:公共宠物{std::string talk()const override{return“woof”;};
类Pig:publicpet{std::string talk()const override{return“oink”;};
宠物制造者
{
//std::deque在添加时从不重新分配
//分发时非常重要的元素
//指向元素的指针
性病:德克猫;
性病:德克犬;
性病:德克猪;
公众:
Pet*make()
{
开关(std::rand()%3)
{
案例0:
猫。安置你回来();
return&cats.back();
案例1:
狗。安置在后面();
返回&dogs.back();
}
猪。安置在后面();
返回&pigs.back();
}
};
int main()
{
std::srand(std::time(0));
宠物制造者;
病媒宠物;
用于(自动i=0;i<100;++i)
宠物。推回(maker.make());
用于(自动宠物:宠物)

Std::CouTalk()>您可能想考虑使用(Boost)变体。它需要调用方额外的步骤,但它可能适合您的需要:

#include <boost/variant/variant.hpp>
#include <boost/variant/get.hpp>
#include <iostream>

using boost::variant;
using std::cout;


struct Pet {
    virtual void print_type() const = 0;
};

struct Cat : Pet {
    virtual void print_type() const { cout << "Cat\n"; }
};

struct Dog : Pet {
    virtual void print_type() const { cout << "Dog\n"; }
};


using PetVariant = variant<Cat,Dog>;
enum class PetType { cat, dog };


PetVariant make_pet(PetType type)
{
    switch (type) {
        case PetType::cat: return Cat();
        case PetType::dog: return Dog();
    }

    return {};
}

Pet& get_pet(PetVariant& pet_variant)
{
    return apply_visitor([](Pet& pet) -> Pet& { return pet; },pet_variant);
}




int main()
{
    PetVariant pet_variant_1 = make_pet(PetType::cat);
    PetVariant pet_variant_2 = make_pet(PetType::dog);
    Pet& pet1 = get_pet(pet_variant_1);
    Pet& pet2 = get_pet(pet_variant_2);
    pet1.print_type();
    pet2.print_type();
}
#包括
#包括
#包括
使用boost::variant;
使用std::cout;
结构Pet{
虚空打印类型()常量=0;
};
结构猫:宠物{
虚空打印类型()常量{cout
是否有一种简洁的方法,任何人都可以将其作为抽象返回,而不必在每次调用函数时都执行
新建
,或者是否有其他方法可以快速创建和返回抽象

TL;DR:函数n Cat Dog
class Pet {
public:
    virtual ~Pet() {}

    virtual void say() = 0;
};

class Cat: public Pet {
public:
    virtual void say() override { std::cout << "Miaou\n"; }
};

class Dog: public Pet {
public:
    virtual void say() override { std::cout << "Woof\n"; }
};

template <>
struct polymorphic_value_memory<Pet> {
    static size_t const capacity = sizeof(Dog);
    static size_t const alignment = alignof(Dog);
};

typedef polymorphic_value<Pet> any_pet;

any_pet pet_factory(std::string const& name) {
    if (name == "Cat") { return any_pet::build<Cat>(); }
    if (name == "Dog") { return any_pet::build<Dog>(); }

    throw std::runtime_error("Unknown pet name");
}

int main() {
    any_pet pet = pet_factory("Cat");
    pet->say();
    pet = pet_factory("Dog");
    pet->say();
    pet = pet_factory("Cat");
    pet->say();
}
Miaou
Woof
Miaou
//  To be specialized for each base class:
//  - provide capacity member (size_t)
//  - provide alignment member (size_t)
template <typename> struct polymorphic_value_memory;

template <typename T,
          typename CA = CopyAssignableTag,
          typename CC = CopyConstructibleTag,
          typename MA = MoveAssignableTag,
          typename MC = MoveConstructibleTag>
class polymorphic_value {
    static size_t const capacity = polymorphic_value_memory<T>::capacity;
    static size_t const alignment = polymorphic_value_memory<T>::alignment;

    static bool const move_constructible = std::is_same<MC, MoveConstructibleTag>::value;
    static bool const move_assignable = std::is_same<MA, MoveAssignableTag>::value;
    static bool const copy_constructible = std::is_same<CC, CopyConstructibleTag>::value;
    static bool const copy_assignable = std::is_same<CA, CopyAssignableTag>::value;

    typedef typename std::aligned_storage<capacity, alignment>::type storage_type;

public:
    template <typename U, typename... Args>
    static polymorphic_value build(Args&&... args) {
        static_assert(
            sizeof(U) <= capacity,
            "Cannot host such a large type."
        );

        static_assert(
            alignof(U) <= alignment,
            "Cannot host such a largely aligned type."
        );

        polymorphic_value result{NoneTag{}};
        result.m_vtable = &build_vtable<T, U, MC, CC, MA, CA>();
        new (result.get_ptr()) U(std::forward<Args>(args)...);
        return result;
    }

    polymorphic_value(polymorphic_value&& other): m_vtable(other.m_vtable), m_storage() {
        static_assert(
            move_constructible,
            "Cannot move construct this value."
        );

        (*m_vtable->move_construct)(&other.m_storage, &m_storage);

        m_vtable = other.m_vtable;
    }

    polymorphic_value& operator=(polymorphic_value&& other) {
        static_assert(
            move_assignable || move_constructible,
            "Cannot move assign this value."
        );

        if (move_assignable && m_vtable == other.m_vtable)
        {
            (*m_vtable->move_assign)(&other.m_storage, &m_storage);
        }
        else
        {
            (*m_vtable->destroy)(&m_storage);

            m_vtable = other.m_vtable;
            (*m_vtable->move_construct)(&other.m_storage, &m_storage);
        }

        return *this;
    }

    polymorphic_value(polymorphic_value const& other): m_vtable(other.m_vtable), m_storage() {
        static_assert(
            copy_constructible,
            "Cannot copy construct this value."
        );

        (*m_vtable->copy_construct)(&other.m_storage, &m_storage);
    }

    polymorphic_value& operator=(polymorphic_value const& other) {
        static_assert(
            copy_assignable || (copy_constructible && move_constructible),
            "Cannot copy assign this value."
        );

        if (copy_assignable && m_vtable == other.m_vtable)
        {
            (*m_vtable->copy_assign)(&other.m_storage, &m_storage);
            return *this;
        }

        //  Exception safety
        storage_type tmp;
        (*other.m_vtable->copy_construct)(&other.m_storage, &tmp);

        if (move_assignable && m_vtable == other.m_vtable)
        {
            (*m_vtable->move_assign)(&tmp, &m_storage);
        }
        else
        {
            (*m_vtable->destroy)(&m_storage);

            m_vtable = other.m_vtable;
            (*m_vtable->move_construct)(&tmp, &m_storage);
        }

        return *this;
    }

    ~polymorphic_value() { (*m_vtable->destroy)(&m_storage); }

    T& get() { return *this->get_ptr(); }
    T const& get() const { return *this->get_ptr(); }

    T* operator->() { return this->get_ptr(); }
    T const* operator->() const { return this->get_ptr(); }

    T& operator*() { return this->get(); }
    T const& operator*() const { return this->get(); }

private:
    polymorphic_value(NoneTag): m_vtable(0), m_storage() {}

    T* get_ptr() { return reinterpret_cast<T*>(&m_storage); }
    T const* get_ptr() const { return reinterpret_cast<T const*>(&m_storage); }

    polymorphic_value_vtable const* m_vtable;
    storage_type m_storage;
}; // class polymorphic_value
//
//  VTable, with nullable methods for run-time detection of capabilities
//
struct NoneTag {};
struct MoveConstructibleTag {};
struct CopyConstructibleTag {};
struct MoveAssignableTag {};
struct CopyAssignableTag {};

struct polymorphic_value_vtable {
    typedef void (*move_construct_type)(void* src, void* dst);
    typedef void (*copy_construct_type)(void const* src, void* dst);
    typedef void (*move_assign_type)(void* src, void* dst);
    typedef void (*copy_assign_type)(void const* src, void* dst);
    typedef void (*destroy_type)(void* dst);

    move_construct_type move_construct;
    copy_construct_type copy_construct;
    move_assign_type move_assign;
    copy_assign_type copy_assign;
    destroy_type destroy;
};


template <typename Base, typename Derived>
void core_move_construct_function(void* src, void* dst) {
    Derived* derived = reinterpret_cast<Derived*>(src);
    new (reinterpret_cast<Base*>(dst)) Derived(std::move(*derived));
} // core_move_construct_function

template <typename Base, typename Derived>
void core_copy_construct_function(void const* src, void* dst) {
    Derived const* derived = reinterpret_cast<Derived const*>(src);
    new (reinterpret_cast<Base*>(dst)) Derived(*derived);
} // core_copy_construct_function

template <typename Derived>
void core_move_assign_function(void* src, void* dst) {
    Derived* source = reinterpret_cast<Derived*>(src);
    Derived* destination = reinterpret_cast<Derived*>(dst);
    *destination = std::move(*source);
} // core_move_assign_function

template <typename Derived>
void core_copy_assign_function(void const* src, void* dst) {
    Derived const* source = reinterpret_cast<Derived const*>(src);
    Derived* destination = reinterpret_cast<Derived*>(dst);
    *destination = *source;
} // core_copy_assign_function

template <typename Derived>
void core_destroy_function(void* dst) {
    Derived* d = reinterpret_cast<Derived*>(dst);
    d->~Derived();
} // core_destroy_function


template <typename Tag, typename Base, typename Derived>
typename std::enable_if<
    std::is_same<Tag, MoveConstructibleTag>::value,
    polymorphic_value_vtable::move_construct_type
>::type 
build_move_construct_function()
{
    return &core_move_construct_function<Base, Derived>;
} // build_move_construct_function

template <typename Tag, typename Base, typename Derived>
typename std::enable_if<
    std::is_same<Tag, CopyConstructibleTag>::value,
    polymorphic_value_vtable::copy_construct_type
>::type 
build_copy_construct_function()
{
    return &core_copy_construct_function<Base, Derived>;
} // build_copy_construct_function

template <typename Tag, typename Derived>
typename std::enable_if<
    std::is_same<Tag, MoveAssignableTag>::value,
    polymorphic_value_vtable::move_assign_type
>::type 
build_move_assign_function()
{
    return &core_move_assign_function<Derived>;
} // build_move_assign_function

template <typename Tag, typename Derived>
typename std::enable_if<
    std::is_same<Tag, CopyAssignableTag>::value,
    polymorphic_value_vtable::copy_construct_type
>::type 
build_copy_assign_function()
{
    return &core_copy_assign_function<Derived>;
} // build_copy_assign_function


template <typename Base, typename Derived,
          typename MC, typename CC,
          typename MA, typename CA>
polymorphic_value_vtable const& build_vtable() {
    static polymorphic_value_vtable const V = {
        build_move_construct_function<MC, Base, Derived>(),
        build_copy_construct_function<CC, Base, Derived>(),
        build_move_assign_function<MA, Derived>(),
        build_copy_assign_function<CA, Derived>(),
        &core_destroy_function<Derived>
    };
    return V;
} // build_vtable
#include<iostream>
#include<utility>

struct Pet {
    virtual ~Pet() = default;
    virtual void foo() const = 0;
};

struct Cat: Pet {
    void foo() const override {
        std::cout << "cat" << std::endl;
    }
};

struct Dog: Pet {
    void foo() const override {
        std::cout << "dog" << std::endl;
    }
};

template<typename T, typename F>
void factory(F &&f) {
    std::forward<F>(f)(T{});
}

int main() {
    auto lambda = [](const Pet &pet) { pet.foo(); };
    factory<Cat>(lambda);
    factory<Dog>(lambda);
}