Nate
Nate

Reputation: 925

C++ scope issue in an if statement: create one of two objects

I've got a homework assignment where I have a base class Package, and two derived classes, OvernightPackage and TwoDayPackage. At one point in the problem I need to create an object of one of those two types, depending on user input. This obviously doesn't work:

        if (shippingOption == OVERNIGHT) {
            OvernightPackage packageObject( // Parameters entered here //);
        }

        else if (shippingOption == TWODAY) {
            TwoDayPackage packageObject( // Parameters entered here //);
        }

Once those if statements execute, I'm out of the scope of those objects, and can't use packageObject. This post helped me understand how I could do it if I was just creating an object of a certain type with different parameters. But the requirements on the assignment are that TwoDayPackage and OvernightPackage need to be different classes.

How would I go about doing this (besides just doing what I need to with the objects in the if statements)?

EDIT: Thanks for helping clear things up, everyone!

Upvotes: 1

Views: 1850

Answers (5)

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145259

Op's code:

if (shippingOption == OVERNIGHT) {
    OvernightPackage packageObject( // Parameters entered here //);
}
else if (shippingOption == TWODAY) {
    TwoDayPackage packageObject( // Parameters entered here //);
}
// Presumably, process the package here.

You might be able to do it like this, using just automatic allocation, which is absolutely best – simplest, safest, most efficient – when it’s applicable:

void process( Package const& package )
{
    // Process the package here.
}

void foo()
{
    // ... whatever, then:

    switch( shippingOption )
    {
    case overnight:
        process( OvernightPackage( blah ) );
        break;
    case twoDay:
        process( TwoDayPackage( blah ) );
        break;
    default:
        assert( false );    // Should never get here.
    }
}

If that's not possible then you can possibly use dynamic allocation of a package object. The trouble is, as a beginner you probably do not know enough to evaluate whether the above is practically impossible (although it can be easy to see when it is indeed possible). Anyway, for dynamic allocation use a smart pointer, such as the C++11 std::shared_ptr:

typedef std::shared_ptr<Package> PackagePtr;

PackagePtr foo( blah, blah, arguments )
{
    // ... whatever, then:

    switch( shippingOption )
    {
    case overnight:
        return PackagePtr( new OvernightPackage( blah ) );
    case twoDay:
        return PackagePtr( new TwoDayPackage( blah ) );
    default:
        assert( false );    // Should never get here.
    }
}

When you get more experience you should strive to always use to most constrained smart pointer available that can do the job, which could be e.g. std::unique_ptr. Because it's easy to transfer ownership to a shared smart pointer but difficult to transfer it back to an ownership pointer. However, as a beginner you will find std::shared_ptr works nicely for most things, that in that respect it's like a potato. ;-)

Upvotes: 2

111111
111111

Reputation: 16148

Like so

#include <memory>
:::

std::unique_ptr<Package> package;

if(shippingOption == OVERNIGHT)
    package.reset(new OvernightPackage(params));
else if (shippingOption == TWODAY)
    package.reset(new TwoDayPackage(params));

package->member;
package->member_function();

If you don't have access to C++11 (see your compiler) you can always use boost::scoped_ptr or if you really have to std::auto_ptr. (if you have gcc/g++ then try compiling with -std=c++0x)

Then advantage of doing it this way over the other listed methods is that memory is managed by RAII semantics, this inline with modern C++ design.

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

http://en.cppreference.com/w/cpp/memory/unique_ptr

http://en.cppreference.com/w/cpp/memory/auto_ptr

Upvotes: 2

Kaz
Kaz

Reputation: 58568

The answer is pointers.

Package *packageConstructor(int type)
{
   switch (type) {
   case OVERNIGHT: return new OvernightPackage(/* ... */);
   case TWODAY: sleep(2 * 86400); /* :) :) :) */ return new TwoDayPackage(/* ... */);
   default: // unrecognized type: handle somehow: return NULL, or throw exception
   } 
}

Once you have pointers, you have to worry about exception safety. It's better to use some smart pointer container instead of raw pointers.

Upvotes: 2

David Schwartz
David Schwartz

Reputation: 182763

One way is to use pointers:

    Package *packageObject = NULL;
    if (shippingOption == OVERNIGHT) {
        packageObject = new OvernightPackage( // Parameters entered here //);
    }

    else if (shippingOption == TWODAY) {
        packageObject = new TwoDayPackage( // Parameters entered here //);
    }

Upvotes: 4

Thomas
Thomas

Reputation: 181745

A variable of type A cannot actually be of a derived type, so (as you noticed) you can't just declare Package packageObject; and assign an OvernightPackage or TwoDayPackage to it (they will be sliced).

However, a pointer to A can point to an instance of a class derived of A. The same goes for references, and (most types of) smart pointers as well.

Upvotes: 3

Related Questions