是否有像 C# 中那样的 C++ new 声明

Is there a C++ new declaration like in C#

我想知道是否有像 C# for C++ 中那样的 new 声明

C# 允许您执行此操作,它只是稍微整理了代码:

FuncCall( new Foo() {
    Bar = "sausage",
    Boo = 4
} );

只是我觉得这在 C++ 中有点草率:

unique_ptr<Foo> foo( new Foo() );
foo.Bar = "sausage";
foo.Boo = 4;

FuncCall( move( foo ) );

Foo 可能看起来像这样:

class Foo
{
public:
    Foo();

    string Bar;
    int Boo;
}

为什么我不把所有都放入构造参数中?

因为当你必须初始化那么多的时候是愚蠢的:

Foo( int width, int height, string title, string className, string thjis, stihjrjoifger gfirejgoirejgioerjgoire ) 它永远持续下去......而我已经在我的 class 中拥有这些属性......所以只是想知道是否可以完成......

可以使用uniform initialization来初始化

您可能会使用 lambda:

FuncCall( []{ Foo f; f.Bar = "sausage"; f.Boo = 4; return f; }() );

Live example

Foo 必须有一个构造函数或者是一个聚合类型:

class Foo {
  std::string bar;
  int boo;
public:
  Foo(std::string s, int i) : bar(std::move(s)), boo(i) {}
};

void FuncCall(std::unique_ptr<Foo> ptr) {}

int main() {
  FuncCall(make_unique<Foo>("sausage", 4));
}

struct Foo {
  std::string bar;
  int boo;
};

void FuncCall(std::unique_ptr<Foo> ptr) {}

int main() {
  std::unique_ptr<Foo> foo(new Foo{"sausage", 4});
  FuncCall(foo);
}

或者你可以避免指针:

void FuncCall(Foo foo) {}

int main() {
  FuncCall({sausage", 4});
}

注意以下几点:

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
    /* ... */
public:
    Foo() {}    // or Foo() = default or elision
};

int main() {
    Foo f;
    f.m_bar = "wunderBar";
}

展开如下:

Foo* fptr = stack_allocate<Foo*>(sizeof(Foo));
// from ctor
fptr->m_bar.string("Bar"); // construct m_bar
fptr->m_baz.int(3);
fptr->m_boo.float(4.2);
// your code:
fptr->m_bar.operator=("wunderBar");

出于类似的原因,您可能想查看 C# 构造的 IL 指令 - 您会发现它执行的操作同样冗余(在更复杂的情况下,可能 boxing/unboxing)。

当您合并不可复制或不可移动的类型时,您的 C++ 方法也会失败,这将迫使您传递指针 and/or 扭曲您的设计。

您/似乎/正在尝试做的是重新创建 Python 的可选参数:

# C++-alike
class Foo(object):
    def __init__(self, Bar, Baz, Boo):
        ...

# C#-alike:
class Foo(object):
    def __init__(self, Bar="Bar", Baz=13, Boo=4.2):
        ...

C++ 没有提供这样做的直接方法,最接近的机制是默认参数和运算符重载:

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
public:
    Foo(std::string bar="Bar", int baz=3, int boo=6.1)
        : m_bar(bar), m_baz(baz), m_boo(boo)
        {}
    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=13, boo=6.1);
     * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2);
     * Foo* f = new Foo("hello", 1, 1.); => new Foo(bar="hello", baz=1, boo=1.);
     * Foo* f = new Foo(42.); => invalid, arguments must be in order.
     */    
};

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
public:
    Foo() = default;

    // allow Foo("hello")
    Foo(const char* bar) : m_bar(bar) {}
    Foo(const std::string& bar) : m_bar(bar) {}
    Foo(std::string&& bar) : m_bar(std::forward(bar)) {}

    // allow Foo(10, 12.)
    explicit Foo(int baz, float boo) : m_baz(baz), m_boo(boo) {}

    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=3, boo=4.2);
     * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2);
     * Foo* f = new Foo(1, 1.); => new Foo(bar="Bar", baz=1, boo=1.);
     * Foo* f = new Foo(42.); => invalid, no match
     */    
};

请参阅 http://ideone.com/yFIqlA 了解 SSCE。

如果您真的有十几种不同的构造函数配置,您可能应该重新考虑您的设计。

--- 编辑 ---

注意:在构造函数中公开所有参数不是强制性的:

class Foo {
    std::string m_user_supplied;
    std::time_t m_time;
public:
    Foo() : m_user_supplied(), m_time(0) {}
    Foo(std::string src) : m_user_supplied(src), m_time(0) {}
    void addTime(time_t inc) { m_time += inc; }
};

--- 编辑 2 ---

"should maybe rethink your design" ... 大型可选参数列表的一个问题是增长。您很可能会得到相互依赖、相互矛盾或相互作用的参数。您可以选择不验证这些,或者最终得到复杂的构造函数。

struct Foo {
    ...
    FILE* m_output;
    const char* m_mode;
    ...

    Foo(..., FILE* output, const char* mode, ...)
    {
        ...
        if (output != nullptr) {
            ASSERT( output == nullptr || mode != nullptr );
            ... other requirements
        } else {
            if (mode != nullptr)
                ... might not be an error but it might be a bug ...
        }
        ...
    }
};

避免这种情况的一种方法是使用 encapsulation/aggregation 相关成员。

class Foo {
    ...
    struct FileAccess {
        FILE* m_file;
        const char* m_mode;
        constexpr FileAccess() : m_file(nullptr), m_mode(nullptr) noexcept {}
        FileAccess(FILE* file, const char* mode) : m_file(file), m_mode(mode) {
            if (file == nullptr || mode == nullptr)
                throw invalid_argument("file/mode cannot be null");
        }
    };
    ...

    FileAccess m_access;

    Foo(..., FileAccess access, ...);
};

这可以公平地减少膨胀。如果你的 API 是稳定的,你可以将它与初始化列表一起使用(如果你的 API 不稳定并且你做改变会咬你的屁股)

auto fooWithFile = make_unique<Foo>{..., /*access=*/{stdout, "w"}, ...};
auto fooWithout  = make_unique<Foo>{..., /*access=*/{}, ...};

如果您随后决定停止使用 ctors 并转而使用 setter,这将转换得相当好,因为您可以重载 "set",它采用各种配置结构之一:

auto foo = make_unique<Foo>();
foo->set(FileAccess(stdout, "w"))
   ->set(Position(Right, -100, Top, 180))
   ->set(DimensionPercentage(80, 75));

auto foo = make_unique<Foo>() { # pseudo based on if C++ had the C# syntax
    m_file = stdout;
    m_mode = "w";
    m_xPosition = -100;
    m_xPositionRel = Right;
    m_yPosition = -180;
    m_yPositionRel = Top;
    m_dimensionType = Percentage;
    m_xDimension = 80;
    m_yDimension = 75;
};