Create objects in conditional c++ statements

前端 未结 7 1258
悲哀的现实
悲哀的现实 2020-12-06 01:33

I am learning c++, and I just got to the object oriented chapter. I have a question about creating objects inside if statements.

The problem I\'m working on says

相关标签:
7条回答
  • 2020-12-06 01:49

    First off, you cannot create an object within a conditional statement and use it after the conditional statement: the two branches of the conditional statement create a scope each and any object created within in destroyed a the end of the branch. That is, you need to come up with a different approach. The simplest approach is probably to delegate the creation of the object to a function which returns the objects as appropriate:

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

    An alternative approach is to use the ternary operator to conditonally create the Report one way or another:

    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();
    

    All of these approaches assume that the Report class is actually copyable.

    0 讨论(0)
  • 2020-12-06 01:50

    I don't know if I understood your question correctly but can't you just declare report before the if/else block and then initialize inside it?

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

    Or in a shorter way:

    Report header; // calls default constructor
    
    if (shouldInitializeWithParams) {
      header = Report(name,company);
    }
    

    Of course this requires you to have the empty constructor defined.

    0 讨论(0)
  • 2020-12-06 01:50

    We don't know whether class Report is copy-able, so better to use pointers.

    Report * header;
    
    if (...) {
      header = new Report();
    else
      header = new Report(name,company);
    
    // after all don't forget
    delete header;
    

    and of course you should use header pointer like that

    header->print_formatted();
    
    0 讨论(0)
  • 2020-12-06 01:53

    As of C++17, you can now use std::optional for this task - it avoids dynamic memory allocation, it avoids two-phase construction of the object, and it doesn't require the type to be movable or copyable. It allows you to delay the construction of the object while keeping it on the stack and also still being exception-safe. If you construct it in every branch you can safely use it afterward with no performance penalty. It will also work as a class member allowing you to avoid the problem with the class constructor initializer for it, unlike the rvalue reference solution. Demo: 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();
    }
    

    If you are stuck in an older compiler that only supports C++11, you can make your own really dumb version of std::optional for this express purpose by using a union and placement new:

    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;
    };
    

    The generated assembly is identical to with std::optional: https://gcc.godbolt.org/z/vzGz9E

    Though, I would recommend using an existing library to supplement your lack of access to std::optional instead of rolling your own like I have done above - my version doesn't work for copying or moving.

    0 讨论(0)
  • 2020-12-06 02:00

    I'm not sure if I understood your question properly. Apologies if you read the answer and realise that that's the case.

    But nevertheless, I think the main strategy would be to make use of constructor overloading. ie, you define constructors for both: the case when no parameters are passed and the case when parameters are passed. The former(what you refer to as default constructor) will initialise the company and report names to default values. The latter will assign the parameters received, to the comapany and report names.

    As for the use of pointers: you can avoid it by 'declaring' a lot of objects of type 'Report'(class). For eg, you can create an array of header(objects). And then you can 'define' it as and when the user responds.

    But by using pointers, you're doing everything in runtime(dynamic assignment) whereas when using arrays(or declaring many objects): the amount is fixed. This can be inefficient.

    0 讨论(0)
  • 2020-12-06 02:02

    The simplest thing that comes to mind is performing a little refactoring on the code flow. Create a function that processes the input and returns the constructed object:

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

    Then call the function from main. The small change in the design is the introduction of a function whose single responsibility is creating a Report from user input, which actually makes sense as a function.

    0 讨论(0)
提交回复
热议问题