开发者

C++: Creating an uninitialized placeholder variable rather than a default object

开发者 https://www.devze.com 2023-01-08 15:10 出处:网络
I\'m moving from Java to C++ right now and I\'m having some difficulties whenever a commonly used concept i开发者_开发百科n Java doesn\'t map directly into C++.For instance, in Java I would do somethi

I'm moving from Java to C++ right now and I'm having some difficulties whenever a commonly used concept i开发者_开发百科n Java doesn't map directly into C++. For instance, in Java I would do something like:

Fruit GetFruit(String fruitName) {
    Fruit fruit;
    if(fruitName == "apple") fruit = new Fruit("apple");
    else if(fruitName == "banana") fruit = new Fruit("banana");
    else fruit = new Fruit("kumquat"); //'cause who really wants to eat a kumquat?

    return fruit;
}

Of course, in C++ the Fruit fruit; statement actually creates a fruit. Does this mean I have to have a default constructor? This seems unsafe! What if my default fruit escaped?


C++ gives you much more headache when it comes to creating fruits. Depending on your needs, you can choose one of the following options:

1) create a Fruit on a stack and return a copy (you need a copy constructor) then and must provide some default fruit in case the name does not match:

Fruit GetFruit(const std::string &name)
{
   if ( name == "banana" ) return Fruit("banana");
   if ( name == "apple" )  return Fruit("apple");
   return Fruit("default");
}

2) create a Fruit on a heap and take care, that there could be null-pointer returned and also remember to delete this fruit somewhere and take care that it is deleted only once and only by its owner (and take care that noone holds a pointer to the deleted fruit):

Fruit* GetFruit(const std::string &name)
{
   if ( name == "banana" ) return new Fruit("banana");
   if ( name == "apple" )  return new Fruit("apple");
   return NULL;
}

3) and finally, use a smart pointer to avoid many possible pointer problems (but take care of null pointers). This option is the closest to your Java programming experience:

typedef boost::shared_ptr<Fruit> FruitRef;

FruitRef GetFruit(const std::string &name)
{
   if ( name == "banana" ) return new Fruit("banana");
   if ( name == "apple" )  return new Fruit("apple");
   return FruitRef();
}


Objects in Java are represented by pointers. Since this is pervasive, there is no special notation for pointers. In C++, objects can be represented by themselves or by pointers, so it's necessary to specify pointers when they occur.

The C++ version of your code is:

Fruit * GetFruit(std::string fruitName) {
    Fruit * fruit = 0;
    if (fruitname == "apple") fruit = new Fruit("apple");
    else if (fruitname == "banana") fruit = new Fruit("banana");
    else fruit = new Fruit("kumquat");

    return fruit;
}

This returns a pointer to Fruit. You would access members like fruit->color(), not fruit.color(). You should delete that pointer when you're through with it.


The most straightforward way would be:

Fruit GetFruit(String fruitName) {
    if(fruitName == "apple") return Fruit("apple");
    else if(fruitName == "banana") return Fruit("banana");
    else fruit = return Fruit("kumquat"); //'cause who really wants to eat a kumquat?
}

... and a direct mapping would be to use a (preferably "smart") pointer:

auto_ptr<Fruit> GetFruit(String fruitName) {
    auto_ptr<Fruit> fruit;
    if(fruitName == "apple") fruit = new Fruit("apple");
    else if(fruitName == "banana") fruit = new Fruit("banana");
    else fruit = new Fruit("kumquat"); //'cause who really wants to eat a kumquat?
    return fruit;
}


To map your example to C++, you should use a pointer. In C++, an object built is considered a valid object (therefore, with reference, you cannot have null).

Using Fruit* fruit = 0; ...

you can have an object defaulted to your need, or 0 defaulted if no test pass.


In C++ there are pointers, which are implicit in Java: there is a difference between an object (that is an entity in memory) and his address. In Java, you need to explicitly create an object, because when you write

MyClass name;

you're creating a reference for an object of that class. This means that name identifies a small memory location which contains the real object address. When you build the whole house using new, it returns only the address, which is assigned to that small memory location.

In C++ there is a better control of memory and you have 2 ways of creating an object: if you use the statement

MyClass object;

This object is created in the stack. This means that when the function returns, the object is destroyed. Note that the object is automatically created, without using the new operator. If you want an object to persist the stack destruction, you shall use the new operator, which returns a pointer to the newly created object:

MyClass *objectPtr = new MyClass();

The * placed after the class name, means you're asking for the relative pointer type instead of an allocation of that object.

Remember you have to clean the memory when you don't need the object anymore, or there will be a memory leak:

delete objectPtr;

So, you can do like this:

MyClass *yourfunction(bool param) {
    if (param)
        return new MyClass(A);
    return new MyClass(B);
}

You shall know, anyway, that Pointers aren't safe at all! Giving the user control over pointers can lead to bad code, bad practices and lot of things that aren't good at all Immediate example: what if you forget to clean the memory after object usage?)

In this case, it's better if you use smart pointers, but now there is really too much to say :) Enjoy googling!

HIH


Your fruit variable in java roughly maps to a C++ pointer. You're correct, you don't want to create the object on the stack, you just want a pointer to the new object that you're creating. So if you just change Fruit to Fruit* this will work (if you change the function return type as well). Just remember that you must later delete the pointer you returned from this function, there's no garbage collection to cleanup your news.


Objects work somewhat differently in Java vs in C++. As you noted, in your code you would default create an object and then you run the risk of it being passed around later. To make the minimum amount of change to your code:

Fruit GetFruit(std::string fruitName) {
    if(fruitName != "apple" && fruitName != "banana")
    {
        fruitName = "kumquat";
    }
    return Fruit(fruitName);
}

However this code will result in the object itself (including all of its internal data) being copied in the return, as well as if its copied around in further use.

To be more Java-esque you would use a boost::shared_ptr instead. Then you're dealing with a reference counted object, just like in Java:

boost::shared_ptr<Fruit> GetFruit(std::string fruitName) {
    if(fruitName != "apple" && fruitName != "banana")
    {
        fruitName = "kumquat";
    }
    return new Fruit(fruitName);
}
0

精彩评论

暂无评论...
验证码 换一张
取 消