我正在学习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 ++中以交互方式进行。到目前为止,我无法在任何地方找到任何类似的问题。
我遇到的最接近的事情是使用指针来做类似于我想做的事情,但是我正在使用的那本书尚未得到指示,我想试着找出一种方法它保持在我正在工作的章节的范围内(即不使用指针)。
我没有包含头文件或类实现文件,因为我认为它们不相关。
提前谢谢!
答案 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::optional
:https://gcc.godbolt.org/z/vzGz9E
尽管如此,我还是建议您使用现有的库来补充您对std::optional
的访问权限,而不是像上面所做的那样滚动您自己的库-我的版本不适用于复制或移动。