Thread: OK, guys, here is my code to stop confusion

  1. #1
    Registered User
    Join Date
    Apr 2007
    Posts
    284

    OK, guys, here is my code to stop confusion

    Sorry for the separate thread. I didn't realize my post last night caused so much confusion.
    Here is the code and compiler error:

    In file base.h
    Code:
    class Base{
      public:
       virtual void foo(string s="")=0;
       virtual ~Base(){}
    };
    In file derived.h
    Code:
    #include "base.h"
    class Derived:public Base{
      public:
      void foo(string s="");
      ~Derived(){}
    };
    In file derived.cc
    Code:
    #include "derived.h"
    void Derived::foo(string s="")
    {
    }
    In file main.cc
    Code:
    #include "base.h"
    #include "derived.h"
    
    Based* ptr = new Derived;
    ptr->foo();
    Error: default argument given to parameter 1 in derived.cc line 3
    Last edited by meili100; 11-29-2007 at 12:59 PM.

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Don't include default arguments in the implentation, only in the declaration. In other words, remove it from derived.cc. Should only say "string s".

  3. #3
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    As I said in the other thread, don't give virtual functions default parameters at all.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  4. #4
    Registered User
    Join Date
    Apr 2007
    Posts
    284
    Quote Originally Posted by CornedBee View Post
    As I said in the other thread, don't give virtual functions default parameters at all.
    I revised the code as what Elysia said. It works well.
    If you are asked to design the code structure, and you are asked to implement the default argument str="", what would you do?

  5. #5
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    As I wrote in the other thread (I think), I'd use the non-virtual interface pattern. Make the function that takes the default argument non-virtual and have it, as its only action, call a virtual function.
    Code:
    class Base
    {
      virtual void do_foo(const std::string &s) = 0;
    public:
      void foo(const std::string &s = "") { do_foo(s); }
    };
    
    class Derived : public Base
    {
      virtual void do_foo(const std::string &s);
    };
    
    void Derived::do_foo(const std::string &s)
    {
      // ...
    }
    Now you may ask, why this complicated structure? The reason is simple. If the virtual function was the one with the default argument, what would happen here?

    Code:
    Derived d;
    d.foo();
    It depends. Did you remember to give the declaration in Derived the default argument, too? Then it will compile and work. Did you forget? It won't compile. Or did you, God forbid, make a typo and gave the parameter a different default value? Then happy bug-hunting. Giving a derived version a different default value means that in
    Code:
    Derived d;
    Base &b = d;
    
    d.foo();
    b.foo();
    the two calls to foo() use different default values for the argument. Fun, huh?

    Whereas with my way, there's only a single place the default value need ever be set, and it will be the same everywhere.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  6. #6
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    OK, so that sample wasn't the best, since you can't call a pure virtual function w/o crashing the program, but I suppose it's an example of different classes using different default arguments.
    But... then again, I'm not exactly sure what it's such a bad thing that functions have a different default argument since it's dependant on the implentation of each class.

  7. #7
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    If the teacher (or similar) tell you to do something, then do that.

    However, there's a good reason to NOT use default parameters for virtual functions. The reason is that you don't actually know which default value you will get:
    Code:
    #include <iostream>
    
    using namespace std;
    
    class base 
    {
    private:
    	int mx;
    public:
    	virtual foo(int x = 1) { mx = x; };
    	virtual print() { cout << "mx = " << mx << endl; };
    };
    
    
    class derived : public base
    {
    public:
    	virtual foo(int x = 2) { base::foo(x); };
    };
    
    
    int main()
    {
    	base *pb;
    	derived *pd;
    
    	pb = new derived;
    	pb->foo();
    	pb->print();
    	pd = new derived;
    	pd->foo();
    	pd->print();
    	cin.ignore();
    	return 0;
    }
    Gives:
    Code:
    mx = 1
    mx = 2
    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Does it treat them as DIFFERENT functions, rather than overriding the base class's function?

  9. #9
    Registered User
    Join Date
    Apr 2007
    Posts
    284
    Quote Originally Posted by matsp View Post
    If the teacher (or similar) tell you to do something, then do that.

    However, there's a good reason to NOT use default parameters for virtual functions. The reason is that you don't actually know which default value you will get:
    Code:
    #include <iostream>
    
    using namespace std;
    
    class base 
    {
    private:
    	int mx;
    public:
    	virtual foo(int x = 1) { mx = x; };
    	virtual print() { cout << "mx = " << mx << endl; };
    };
    
    
    class derived : public base
    {
    public:
    	virtual foo(int x = 2) { base::foo(x); };
    };
    
    
    int main()
    {
    	base *pb;
    	derived *pd;
    
    	pb = new derived;
    	pb->foo();
    	pb->print();
    	pd = new derived;
    	pd->foo();
    	pd->print();
    	cin.ignore();
    	return 0;
    }
    Gives:
    Code:
    mx = 1
    mx = 2
    --
    Mats
    Really? I guess
    base *pb;
    pb = new derived;
    pb->foo();
    pb->print();
    should print 2 because pb->foo(); calls the virtual function if derived through the VTable.

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by Elysia View Post
    OK, so that sample wasn't the best, since you can't call a pure virtual function w/o crashing the program, but I suppose it's an example of different classes using different default arguments.
    If you're referring to my example, the second call is polymorphic and will call Derived::foo, not crash. The OP's sample code is equally valid.
    Runtime calls to pure functions don't happen in normal operation - only when you're doing something weird. (Most typically, calling a pure virtual function in a constructor or destructor. Although compilers ought to be able to catch that.)

    But... then again, I'm not exactly sure what it's such a bad thing that functions have a different default argument since it's dependant on the implentation of each class.
    It's bad because it's unintuitive. Unintuitive is bad. I expect my call to a virtual function that looks exactly the same as my other call to the same virtual function to have exactly the same result. Sure, I call one through the base interface and one through the derived interface, but it's virtual, so it doesn't matter, right? Right?

    Quote Originally Posted by meili100 View Post
    Really? I guess
    Code:
    	base *pb;
    	pb = new derived;
    	pb->foo();
    	pb->print();
    should print 2 because pb->foo(); calls the virtual function if derived through the VTable.
    You've just proven my point by being wrong. Default arguments have nothing at all to do with the vtable or virtual functions. It's something that the compiler substitutes into your call, at compile time, if you omit the parameter. That is, this:
    Code:
    void foo(int i = 0);
    
    int main()
    {
      foo();
    }
    is, to the compiler, exactly the same as this:
    Code:
    void foo(int i);
    
    int main()
    {
      foo(0);
    }
    The compiler just places the default argument into the argument list. The function being a virtual member function doesn't change that. It looks as the static type of the expression, chooses the signature of the corresponding function and substitutes any default parameters it finds.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Try it!

    The reason is, the compiler decides what default to use at compile-time. At compile-time, the compiler only knows what the type of the pointer is [1].

    That's also the reason why you need it in the declaration, but not in the definition - the definition may well be in a separate .cpp file, but the declaration and the default is in a header-file included everywhere you use the class.

    When you write somehting like:
    Code:
    ...
    virtual foo(int x = 1) { mx = x; };
    ...
    pb->foo();
    The compiler isn't calling foo with no argument. It replaces your empty argument with 1, before calling the function. So the compiler must use what it knows about pb to figure out what default foo has.

    And this is why you SHOULDN'T use defaults with virtual functions, because you don't know which default is being used [well, of course you do, but it may not be the one that you first think it is].

    [1] Of course, in this simple case, the compiler COULD figure out what the actual class is, as it's created just before the call - but it still uses the pointer to find which default to use, rather than the actual class pointed to.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 1
    Last Post: 03-21-2006, 07:52 AM
  2. Confusion : Warning long code
    By mart_man00 in forum C Programming
    Replies: 10
    Last Post: 04-08-2003, 08:07 PM
  3. True ASM vs. Fake ASM ????
    By DavidP in forum A Brief History of Cprogramming.com
    Replies: 7
    Last Post: 04-02-2003, 04:28 AM
  4. Seems like correct code, but results are not right...
    By OmniMirror in forum C Programming
    Replies: 4
    Last Post: 02-13-2003, 01:33 PM
  5. Interface Question
    By smog890 in forum C Programming
    Replies: 11
    Last Post: 06-03-2002, 05:06 PM