我可以在 C++ 工厂中声明的同一行使用变量吗?

2024-01-01

我有一个多态的类层次结构。虽然我也支持标准工厂方法,即仅使用基类指针,但我还想要一个为我提供派生类的工厂机制,这并不容易,因为这些函数仅在返回类型上有所不同。这就是为什么我想到重载函数并让编译器选择正确的函数的原因。

一个简单的应用是,我可以编写函数来创建派生对象,“准备”它并返回指向它的基指针,以便在不再需要类型信息时进行进一步访问。

  • 问题1:下面这个可以吗?
  • 问题2:newObject()的参数只是为了让编译器选择正确的函数。我担心使用p1在声明的同一行。在设置它之前我从未读取过它的值newObject(),但我不确定它是否会导致未定义的行为。我还有一个自我分配,因为返回值被分配给自己......

这是代码示例:

class Base
{
  public:
    virtual ~Base(void){}
};

class Derived1 : public Base
{
  public:
    virtual ~Derived1(void){}
};

class Derived2 : public Base
{
  public:
    virtual ~Derived2(void){}
};

// factory

Base * newObject(int i)
{
    if (i == 1)
    {
        return new Derived1();
    }
    else
    {
        return new Derived2();
    }
}

// family of functions to create all derived classes of Base

Derived1 * newObject(Derived1 *& p)
{
    p = new Derived1();
    return p;
}

Derived2 * newObject(Derived2 *& p)
{
    p = new Derived2();
    return p;
}

int main()
{
    // compiler picks the right newObject function according to the parameter type
    Derived1 * p1 = newObject(p1);
    Derived2 * p2 = newObject(p2);

    // This is safe, right? But it does not convey that it creates something. Hence I prefer the above syntax.
    Derived2 * p3 = nullptr;
    newObject(p3);

    delete p3;
    delete p2;
    delete p1;
}

EDIT:

为了避免使用刚刚创建的变量的问题,这是一种替代方法:

Derived1 * newObject(Derived1 *)
{
    // anonymous variable is not used at all, just to pick the right function
    Derived1 * p = new Derived1();
    return p;
}

这是一个有问题的设计,但从纯粹语言的角度来看是正确的。 C++ 标准是这么说的:

[基本生活] http://eel.is/c++draft/basic.life

1 对象或引用的生命周期是其运行时属性 对象或参考。一个物体被称为非空的 初始化(如果它是类或聚合类型)并且它或其中之一 它的子对象是由一个构造函数而不是一个简单的构造函数初始化的 默认构造函数。 T 类型对象的生命周期从以下时间开始:

  • 获得类型 T 具有适当对齐和大小的存储,并且
  • 如果对象具有非空初始化,则其初始化已完成,

...

7 在对象的生命周期开始之前但在存储之后 对象将占用的空间已分配或在生命周期结束后 对象的存储已经结束并且在该对象的存储之前 占用被重用或释放,任何引用的左值 可以使用原始对象,但只能以有限的方式使用......例如glvalue 指分配的存储空间,并使用泛左值的属性 不依赖于它的值是明确定义的。该计划有 未定义的行为,如果:

  • 左值用于访问对象,或者
  • ...

第 7 段明确指定使用这样的引用来分配给生命周期尚未开始的对象是 UB。

但根据第 1 段,指针的生命周期从为其分配存储空间时开始,因为它是一种空初始化的类型。所以本质上,你的代码并没有受到第 7 段的威胁。


如果调用时不想指定模板参数newObject(如果您使用 C++11,这并没有那么糟糕或冗长auto p = newObject<Dervied1>()),并且您至少可以访问 C++11,您可以使用常见的模板相关技巧来推迟构造,直到类型已知为止。它的主要内容是:

namespace detail {
// ...
template<typename... Args>
struct DefferedConstruct {
    std::tuple<Args&...> args_tuple;

    template<typename T>
    operator T*() {
        return new T(make_from_tuple<T>(
            args_tuple
        ));
    }
};
} // namespace detail

template<typename... Args>
auto newObject(Args&&... args) -> detail::DefferedConstruct<Args...> {
    return detail::DefferedConstruct<Args...>{
        std::forward_as_tuple(args...)
    };
}

class Base
{
  public:
    virtual ~Base(void){}
};

class Derived1 : public Base
{
  public:
    Derived1(int) {}
    virtual ~Derived1(void){}
};

class Derived2 : public Base
{
  public:
    virtual ~Derived2(void){}
};

int main()
{
// compiler construct the right object according to the return type
    Derived1 *p1 = newObject(1);
    Derived2 *p2 = newObject();

    delete p1;
    delete p2;
}

Live Example http://ideone.com/ilP0gq

上面的代码只是使用了大量的 C++ 魔法来拖拽构造函数参数,直到在函数退出时知道要构造的类。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我可以在 C++ 工厂中声明的同一行使用变量吗? 的相关文章

  • MongoDB C# 驱动程序检查身份验证状态和角色

    这是我使用 MongoDB 身份验证机制登录 MongoDB 的代码 try var credential MongoCredential CreateMongoCRCredential test admin 123456 var sett
  • Web UI 中的 .Result 出现死锁

    我正在阅读以下主题http blog stephencleary com 2012 07 dont block on async code html http blog stephencleary com 2012 07 dont bloc
  • LINQ to XML - 如何正确使用 XDocument

    现在我首先要说的是 这确实是一项任务 然而 在我遇到 Linq to XML 语法之前 我几乎已经完成了它 我有 2 个课程 曲目和 CD 现在作为作业的一部分 我创建了一张 CD 然后向其中添加了一些曲目 在搜索了大量完美解释了如何从 x
  • 测试 hdf5/c++ 中的组是否存在

    我正在打开一个现有的 HDF5 文件来附加数据 我想向那个叫做的小组保证 A存在以供后续访问 我正在寻找一种简单的方法来创建 A有条件地 如果不存在则创建并返回新组 或者返回现有组 一种方法是测试 A存在 我怎样才能高效地做到这一点 根据
  • boost线程在中断时不打印退出消息

    我有这段代码用于执行三个线程 其中第二个线程应在按 Enter 时中断并打印退出消息 void input val DO STUFF return void process val DO STUFF try cout lt lt waiti
  • 在 Windows Phone 上启动 pdf 文件时出现 System.Runtime.InteropServices.COMException

    我正在尝试使用我之前在另一个应用程序上使用过的以下工作代码打开 pdf 文件 但这一次 当流程到达此行时 我收到 System Runtime InteropServices COMException Windows System Laun
  • Linq Where 本地计数器关闭在 VS watch 中的结果不同

    我尝试删除前 3 个元素array与 LinQWhere扩展功能 这是一个例子 var array new 1 2 3 4 5 6 7 8 9 var count 3 var deletedTest1 0 var test1 array W
  • 使用 VSTO 更改 Outlook 设置

    我刚刚花了大约 4 个小时试图弄清楚如何以编程方式检索 设置 Microsoft Outlook 2010 的 Outlook 设置 我所说的 设置 是指文件 选项 邮件下的设置 我想做的是检索用户设置的设置列表 自动化我们每天需要在某些消
  • glDrawElements 只绘制半个四边形

    这是我的功能 void Object draw2 if mIsInitialised return Tell OpenGL about our vertex and normal data glEnableClientState GL VE
  • 防止复制构造和返回值引用的分配

    如果我有一个函数返回对类实例的引用 但我无法控制其源 比如说list
  • 推送 Lua 表

    我已经创建了一个Lua表C 但我不知道如何将该表推入堆栈顶部 以便我可以将其传递给 Lua 函数 有谁知道如何做到这一点 这是我当前的代码 lua createtable state libraries size 0 int table i
  • 使用 Linq 进行异步Where过滤

    我有一个List通过填充的元素async调用 WebService 没问题 我需要过滤该列表以便在应用程序视图上显示某些内容 我试过这个 List
  • 如何使用 Clang 查找内存泄漏

    我在我的机器 ubuntu 中安装了 Clang 以便发现我的 C 代码中的内存泄漏 我编写了一个示例代码来检查它的工作情况 如下所示 File hello c for leak detection include
  • 删除对象时指针自动指向空

    假设我有一个对象和其他几个不同类类型的对象中的 10 个指向它的指针 如果对象被删除 这些指针必须设置为空 通常我会将对象的类与具有指向它的指针的类互连 以便它可以通知它们它正在被删除 并且它们可以将它们的指针设置为空 但这也有一个负担 即
  • 宏观评价[重复]

    这个问题在这里已经有答案了 可能的重复 未定义的行为和序列点 https stackoverflow com questions 4176328 undefined behavior and sequence points 我无法理解以下宏
  • 多个同名内存数据库

    关系到这个答案 https stackoverflow com a 48446491 596758 我试图通过设置让多个上下文工作UseInMemoryDatabase以同名 下面的测试失败 第二个上下文为空 我还需要做什么才能在内存数据库
  • 如何使用 g++ 在 c++ 20 中使用模块?

    我读了这个链接https gcc gnu org wiki cxx modules https gcc gnu org wiki cxx modules并尝试从该网站复制以下示例 我已经知道这个编译器部分支持模块系统 注 我用的是windo
  • 如何使复选框不可选择?

    我想知道你是怎么做的CheckBox在c 中无法选择 我认为这会是类似 SetSelectable false 之类的东西 但我似乎看不到该方法 I found CanSelect但这似乎是只读属性 您可以设置自动检查 http msdn
  • C++0x 中的新 unicode 字符

    我正在构建一个 API 它允许我获取各种编码的字符串 包括 utf8 utf16 utf32 和 wchar t 根据操作系统 可能是 utf32 或 utf16 新的 C 标准引入了新类型char16 t and char32 t没有这么
  • 当我读取 500MB FileStream 时出现 OutOfMemoryException

    我使用 Filestream 读取大文件 gt 500 MB 但出现 OutOfMemoryException 任何有关它的解决方案 我的代码是 using var fs3 new FileStream filePath2 FileMode

随机推荐