在条件c ++语句中创建对象

时间:2012-02-19 03:38:42

标签: c++ class object if-statement constructor

我正在学习c ++,而我刚刚进入了面向对象的章节。我有一个关于在if语句中创建对象的问题。

我正在处理的问题是创建一个将显示报告标题的类。该类有一个默认构造函数,它将公司名称和报告名称设置为泛型,并且如果用户需要,还有一个带有两个参数的构造函数(字符串公司名称和报告名称)。

问题是,具体而言,“双参数默认构造函数应该允许在创建新的Report对象时指定这些[公司和报告名称]。如果用户创建Report对象而不传递任何参数,使用默认值。否则,使用用户指定的名称值。“

所以我的问题是,如何创建这些对象?我理解如何创建没有任何参数的对象(即Report newobj;),以及参数(即Report newobj(string string);)。基本上,我首先了解如何在main函数的顶部创建这些对象。但是可以根据用户选择在if语句中创建它们吗?这是我到目前为止所做的,显然,它不起作用:

#include <iostream>
#include <string>
#include "report.h"
using namespace std;

bool enter_company_name();           // return true if user wants to enter company name
bool print_form();              // return true if user wants to print in formatted output

int main()
{
  string company_name,
    report_name;
  bool name = false,
    format = false;

  name = enter_company_name();
  format = print_form();

  if (name)
    {
      cout << "Enter company name: ";
      getline(cin, company_name);
      cout << "Enter report name: ";
      getline(cin, report_name);
      Report header(company_name, report_name);  // THIS IS MY PROBLEM
    }
  else
      Report header;  // THIS IS MY PROBLEM

  if (format)
    header.print_formatted();
  else
    header.print_one_line();

  return 0;
}

bool enter_company_name()
{
  char choice;

  cout << "Do you want to enter a name?\n>";
  cin >> choice;

  if (choice == 'y' || choice == 'Y')
    return true;
  else
    return false;
}

bool print_form()
{
  char choice;

  cout << "Do you want to print a formatted header?\n>";
  cin >> choice;

  if (choice == 'y' || choice == 'Y')
    return true;
  else
    return false;
}

所以我想使用默认值创建一个对象(如果没有指定),或者如果给出了选择,则创建一个带有用户值的对象。我只是无法弄清楚如何在c ++中以交互方式进行。到目前为止,我无法在任何地方找到任何类似的问题。

我遇到的最接近的事情是使用指针来做类似于我想做的事情,但是我正在使用的那本书尚未得到指示,我想试着找出一种方法它保持在我正在工作的章节的范围内(即不使用指针)。

我没有包含头文件或类实现文件,因为我认为它们不相关。

提前谢谢!

7 个答案:

答案 0 :(得分:15)

首先,你不能在条件语句中创建一个对象并在条件语句之后使用它:条件语句的两个分支创建一个范围,并且在其中创建的任何对象都销毁了分支的末尾。也就是说,你需要提出一种不同的方法。最简单的方法可能是将对象的创建委托给一个函数,该函数根据需要返回对象:

Report makeReport() {
    if (enter_company_name()) {
        ...
        return Report(name, company);
    }
    return Report();
}

...
Report report = makeReport();

另一种方法是使用三元运算符以某种方式条件地创建Report

bool get_company_name = enter_company_name();
std::string name(get_company_name? read_name(): "");
std::string company(get_company_name? read_company(): "");
Report report = get_company_name? Report(name, company): Report();

所有这些方法都假设Report类实际上是可复制的。

答案 1 :(得分:9)

我不知道我是否正确理解了您的问题,但是您不能在if / else块之前声明报告然后在其中初始化吗?

Report header;

if (...) {
  header = Report();
else
  header = Report(name,company);

或以较短的方式:

Report header; // calls default constructor

if (shouldInitializeWithParams) {
  header = Report(name,company);
}

当然,这需要您定义空构造函数。

答案 2 :(得分:4)

我们不知道类Report是否可以复制,所以最好使用指针。

Report * header;

if (...) {
  header = new Report();
else
  header = new Report(name,company);

// after all don't forget
delete header;

当然你应该使用像这样的header指针

header->print_formatted();

答案 3 :(得分:2)

最简单的想法是对代码流进行一些重构。创建一个处理输入并返回构造对象的函数:

Report loadReport() {
    if (user_input()) {
        // read input
        return Report(name,company);
    } else {
        return Report();
    }
}

然后从main调用该函数。设计中的一个小变化是引入了一个函数,其职责是从用户输入创建Report,这实际上是一个函数。

答案 4 :(得分:0)

我不确定我是否理解你的问题。如果您阅读答案并意识到情况属实,请道歉。

但是,我认为主要策略是利用构造函数重载。即,您为两者定义构造函数:没有传递参数的情况和传递参数的情况。 前者(您称之为默认构造函数)将初始化公司并将名称报告为默认值。后者会将收到的参数分配给comapany和报告名称。

至于指针的使用:你可以通过'声明'很多'Report'(类)类型的对象来避免它。例如,您可以创建一个标题(对象)数组。 然后,您可以在用户响应时“定义”它。

但是通过使用指针,您在运行时(动态分配)中执行所有操作,而在使用数组(或声明许多对象)时:数量是固定的。这可能效率低下。

答案 5 :(得分:0)

您可以使用右值引用将其绑定到任何一个构造对象。

struct Foo
{
  Foo(int bar): bar(bar) {}

  int bar
};

Foo&& f = condition ? Foo(4) : Foo(5);

f.bar = 1000; 

答案 6 :(得分:0)

从C ++ 17开始,您现在可以使用std::optional来完成此任务-它避免了动态内存分配,避免了对象的两阶段构造,并且不需要类型是可移动的或可复制。它允许您延迟对象的构造,同时将其保留在堆栈上,并且仍然是异常安全的。如果您在每个分支中构造它,那么以后可以放心使用它,而不会影响性能。它也可以作为类成员使用,与右值引用解决方案不同,它可以避免类的构造函数初始化程序出现问题。演示:https://gcc.godbolt.org/z/vbe5eh

#include <optional>

struct UseCtorA final {};
struct UseCtorB final {};
struct Report final
{
    Report() = delete;
    Report(Report const &) = delete;
    Report(Report &&) = delete;
    Report &operator=(Report const &) = delete;
    Report &operator=(Report &&) = delete;

    Report(UseCtorA, char c) : v{1} { if(c == 't'){ throw 3; } }
    Report(UseCtorB) : v{2} {}

    constexpr auto getValue() const noexcept { return v; }

private:
    int v;
};

int main(int nargs, char const *const *args)
{
    std::optional<Report> report;
    if(nargs > 2)
    {
        report.emplace(UseCtorA{}, args[1][0]);
    }
    else
    {
        report.emplace(UseCtorB{});
    }
    return report->getValue();
}

如果您被困在仅支持C ++ 11的较旧的编译器中,则可以通过使用并集和新的放置来创建自己的std::optional的哑本版本,以达到此明确的目的:

struct Empty final {};
template<typename T>
struct Optional
{
    Optional() noexcept : unused{} {}
    ~Optional() noexcept(noexcept(v.~T()))
    {
        if(constructed_successfully)
        {
            v.~T();
        }
    }
    template<typename... Args>
    auto emplace(Args &&... args) -> T &
    {
        if(constructed_successfully)
        {
            v.~T();
            constructed_successfully = false;
        }
        T &r = *new (&v) T(std::forward<Args>(args)...);
        constructed_successfully = true;
        return r;
    }
    auto operator->() noexcept -> T *
    {
        return &v;
    }

private:
    union
    {
        T v;
        [[no_unique_address]] Empty unused;
    };
    bool constructed_successfully = false;
};

生成的程序集与std::optionalhttps://gcc.godbolt.org/z/vzGz9E

相同

尽管如此,我还是建议您使用现有的库来补充您对std::optional的访问权限,而不是像上面所做的那样滚动您自己的库-我的版本不适用于复制或移动。