Thread: Help understanding how "operator char*()" works.

  1. #1
    Registered User Lawn Gnomusrex's Avatar
    Join Date
    Oct 2008
    Location
    Leading lawn gnomes on the path to world domination! ;o)
    Posts
    13

    Question Help understanding how "operator char*()" works.

    Hello, everyone!
    Sorry to be a bother, but I needed some help wrapping my head around some syntax.

    I would like to apologize in advance for the long post.

    The book I'm reading from right now is trying to teach me how to make a class similar to #include <string>. However, there is some strange syntax that I'm not understanding.
    I've never posted here before, much less posted code, so please forgive me if I don't provide all the information you need.

    Also, I'm fairly new at programming. I've only been learning this for about 5 months or so.

    Code:
    class my_string {
         private:
              char *ptr;
         public:
              //Constructors and destructors that I understand, and functions I understand, so I
              //see no need to post any of the code I do understand.
         
              my_string& operator=(const my_string &object)
                   {cpy(object.ptr); return *this;} 
    
              my_string& operator=(char *s)
                   {cpy(char *s); return *this;}
    
              operator char*() {return ptr;}
    
              void cpy(char *s)
    };
    
    void my_string::cpy(char *s) {
         delete [] ptr;
         int n = strlen(s);
         ptr = new char[n + 1];
         strcpy(ptr, s);
    }
    I don't understand why it is written this way:
    Code:
    my_String& operator=(const my_string &object);
    I know "&object" is a reference, but I don't fully understand what "my_string&" means, is it referencing something? If it is then what is it referencing? If it's something other than a reference, can someone please explain what it means? Also, why is there only one definition of cpy? and not something like:
    Code:
    void cpy(char *s);
    void cpy(const my_string &object);
    also I don't understand exactly how:
    Code:
    operator char*()
    works. I know it has something to do with converting the "my_string" object to a char*(ptr) for use with cout, but I don't understand exactly how.

    Anyways, thank you to anyone who takes the time to answer this, and I apologize if this is something simple. I'm learning on my own, so I don't have a teacher or professor to ask. If I didn't provide enough information, or ask my questions in any coherent way, please let me know so I can try to clarify what I'm asking.

  2. #2
    The superhaterodyne twomers's Avatar
    Join Date
    Dec 2005
    Location
    Ireland
    Posts
    2,273
    That code doesn't work. This should...
    Code:
    class my_string {
      private:
        char *ptr;
    
      public:
        my_string& operator=(const my_string &object) {
          cpy(object.ptr); return *this;
        } 
    
        my_string& operator=(char *s) {
          cpy(s); // Was cpy(char *s);
          return *this;
        }
    
        operator char*(void) {
          return ptr;
        }
    
        void cpy(char *s); // Don't forget ;
    };
    
    void my_string::cpy(char *s) {
         delete [] ptr;
         int n = strlen(s);
         ptr = new char[n + 1];
         strcpy(ptr, s);
    }
    
    int main()
    {
      my_string ms;
      ms.cpy( "test" );
      std::cout<< (char*)ms; // Never overloaded this before, but I figure this is how it should work.
                             // or the static_cast<char*>( ms ); equivalent.
    
    	return 0;
    }
    >> I don't fully understand what "my_string&" means
    That defines the return type of a function. Replace it, for a moment, with 'int'. The function returns an integer variable. If you change it to int& it returns a reference to an integer. So my_string& returns a reference to an instance of a my_string object. That's what you want with an = operator.

    >> Also, why is there only one definition of cpy?
    I'm assuming that's not all the code there is. There probably should be. Write it yourself as a challenge.

  3. #3
    Registered User Lawn Gnomusrex's Avatar
    Join Date
    Oct 2008
    Location
    Leading lawn gnomes on the path to world domination! ;o)
    Posts
    13
    Yeah, I was really only confused about
    Code:
    my_String& operator=(const my_string &object);
    and
    Code:
    operator char*() {return ptr;}
    I figured giving you guys some of the code would help explain what these were. Like i said I'm not used to posting here, and since I don't fully understand what I'm asking, I don't know what is relevant or not.

    Thank you for explaining my_string& for me, I really appreciate it!

    I'm still unsure how operator char* works, or what parts of the code are relevant to showing you where it is involved or not. So I copied down the entire code.
    I'm new so I'm unsure if all of this is even needed or not. If it isn't needed then I apologize for pasting the following monolith of code.

    Code:
    class String {
    private:
        char *ptr;
    public:
        String();
        String(char *s);
        String(const String &src);
        ~String();
    
        String& operator=(const String &src)
            {cpy(src.ptr); return *this;}
    
        String& operator=(char *s)
            {cpy(s); return *this;}
    
        String operator+(char *s);
        int operator==(const String &other);
        operator char*() {return ptr;}
    
        void cat(char *s);
        void cpy(char *s);
    };
    
    int main() {
        String a, b, c;
        a = "I ";
        b = "am ";
        c = "so ";
        String d = a + b + c + "very happy!\n";
        cout << d;
        return 0;
    }
    
    // ----------------------------------
    // STRING CLASS FUNCTIONS
    
    String::String() {
        ptr = new char[1];
        ptr[0] = '\0';
    }
    
    String::String(char *s) {
        int n = strlen(s);
        ptr = new char[n + 1];
        strcpy(ptr, s);
    }
    
    String::String(const String &src) {
        int n = strlen(src.ptr);
        ptr = new char[n + 1];
        strcpy(ptr, src.ptr);
    }
    
    String::~String() {
        delete [] ptr;
    }
    
    int String:: operator==(const String &other) {
        return (strcmp(ptr, other.ptr) == 0);
    }
    
    String String::operator+(char *s) {
        String new_str(ptr);
        new_str.cat(s);
        return new_str;
    }
    
    // cpy -- Copy string function
    //
    void String::cpy(char *s) {
        delete [] ptr;
        int n = strlen(s);
        ptr = new char[n + 1];
        strcpy(ptr, s);
    }
    
    // cat -- Concatenate string function
    //
    void String::cat(char *s) {
    
        // Allocate sufficient room for new string data.
    
        int n = strlen(ptr) + strlen(s);
        char *p1 = new char[n + 1];
    
        // Copy data to this new memory block.
    
        strcpy(p1, ptr);
        strcat(p1, s);
    
        // Release old memory block and update ptr.
    
        delete [] ptr;
        ptr = p1;
    }

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    I still don't quite know what your question is, but we'll try this:
    operator char*() is the old-style C casting: just like you can cast an integer to float by (float)int_var, you can cast to char* as (char*)string_var. It's possible this may also be used for parameter-matching, i.e., if a function takes a char *, this can be used to convert your string into that parameter. (I don't know for sure if that's the mechanism; someone will be along soon to set me (and you) straight, but that would explain why you're talking about cout and <<.)

    If you're familiar with the STL std::string, then this operator char*() is doing basically the same job as .c_str() there.

  5. #5
    Registered User Lawn Gnomusrex's Avatar
    Join Date
    Oct 2008
    Location
    Leading lawn gnomes on the path to world domination! ;o)
    Posts
    13
    If you remove this line:
    Code:
    operator char*() {return ptr;}
    the entire program won't work at all, it won't output any of the variables with cout.
    I'm just wondering how
    Code:
    operator char*() {return ptr;}
    lets this string class interact with cout, and why if you remove this line of code, it all falls apart.

    And I'm not at all familiar with STL libraries. sorry.

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Because cout knows how to print char*. It doesn't know how to print MyStrings, until either (1) you define how to convert MyStrings into char* -- this is what you did, or (2) you define << to work on MyStrings -- this is what most people do.

  7. #7
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    operator char*() is a function that allows the object to be implicitly converted to a char* type. Those types of operators should be avoided though, since they can have bad interactions in your code.
    See Chapter 40. Avoid providing implicit conversions
    "I am probably the laziest programmer on the planet, a fact with which anyone who has ever seen my code will agree." - esbo, 11/15/2008

    "the internet is a scary place to be thats why i dont use it much." - billet, 03/17/2010

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Those types of operators should be avoided though, since they can have bad interactions in your code.
    No. They should not be avoided. If you do the work to block otherwise logically illegal, or just silly, operations they can make code much cleaner. (Of course, misused they can make code uglier, but that doesn't say much as it applies to most features of C++.)

    Edit:

    "Give a man a fire and he's warm for a day. Light a man on fire and he's warm for the rest of his life!" - Edgar Frog, Lost Boys: The Tribe
    I know this is off-topic, but just so as you know: that is actually, or at least earlier, a quote from Terry Pratchett's "Discworld" series. (Specifically, the quote is from "Jingo" as quoted by the character Solid Jackson.)

    Soma

  9. #9
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by phantomotap View Post
    No. They should not be avoided. If you do the work to block otherwise logically illegal, or just silly, operations they can make code much cleaner. (Of course, misused they can make code uglier, but that doesn't say much as it applies to most features of C++.)
    What if you have a String class that has both an operator char*() and an operator+() for concatenating strings? Can you guarantee that things like:
    Code:
    str1 + "something"
    will always use the operator+() instead of converting str1 to a char* and adding 2 pointers?
    "I am probably the laziest programmer on the planet, a fact with which anyone who has ever seen my code will agree." - esbo, 11/15/2008

    "the internet is a scary place to be thats why i dont use it much." - billet, 03/17/2010

  10. #10
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    What if you have a String class that has both an operator char*() and an operator+() for concatenating strings?
    O_o

    That was a spectacularly bad choice. If you have a string class that supports null-terminated 'const char *' strings then you should have 'string_type operator + (const string_type &, const char *)' anyway.

    Can you guarantee that [...] always use the operator+() instead of converting str1 to a char* and adding 2 pointers?
    If you have 'string_type(const char *)' and 'string_type operator + (const string_type &, const string_type &)', 'const char * operator + (const char *, const char *)' is not an option--technically not a "better fit".

    Soma

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by phantomotap
    That was a spectacularly bad choice.
    For a good example of why one should avoid writing such conversion functions, read GotW #19: Automatic Conversions.

    EDIT:
    Actually, I notice that cpjust linked to an online copy of a book that incorporates material from the GotW articles. The point is, you cannot "do the work to block otherwise logically illegal, or just silly, operations" if you have no control over the operations of the converted type, and this is certainly true of the built-in types, pointer types, and types provided by some library not under your control.
    Last edited by laserlight; 10-05-2008 at 01:02 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    For a good example of why one should avoid writing such conversion functions, read GotW #19: Automatic Conversions.
    O_o

    No. That doesn't provide a good reason to avoid silent conversion and neither does the book equivalent. It only gives an excuse for lazy programmers to avoid them.

    Inheritance and multiple inheritance, especially the "Curiously Recurring Template Pattern", can interfere with overload resolution in the exact same ways. Actually, the implicit conversion provided on the constructor side of things, which 'std::string' provides, can yield similarly unexpected results.

    As for logically illegal or just silly operations--once again: write the code.

    <edit>

    The point is, you cannot "do the work to block otherwise logically illegal, or just silly, operations" if you have no control over the operations of the converted type, and this is certainly true of the built-in types, pointer types, and types provided by some library not under your control.
    ^_^

    Ha! I always find fun stuff here.

    Who said to control the operations of the converted type? (I certainly didn't.)

    </edit>

    <edit>

    Oh, and just for fun: You can absolutely prevent every misuse referenced by "C++ Coding Standards" by doing the work, but the fun part is you don't even have to do the work but once: you can use the "Curiously Recurring Template Pattern" to provide an easy fix for any future classes.

    </edit>

    Soma
    Last edited by phantomotap; 10-05-2008 at 01:25 AM.

  13. #13
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Lawn Gnomusrex View Post
    Yeah, I was really only confused about
    Code:
    my_String& operator=(const my_string &object);
    Remember, my young friend:
    The world is full of programmers who seem to think that the & should bind to the name while it really should not (note: joke)!
    my_String& var, my_String & var and my_String &var mean the same thing.

    and
    Code:
    operator char*() {return ptr;}
    Basically, it tells the compiler of a way to convert my_String to char*. So when the compiler sees a function that takes char*, it tries to convert my_String to char* and finds an overloaded operator that can do the work. Then it works.
    But as cpjust mentioned, they should be avoided because they usually have nasty consequences.
    It's a shame, really.
    Last edited by Elysia; 10-05-2008 at 01:26 AM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by phantomotap
    No. That doesn't provide a good reason to avoid silent conversion and neither does the book equivalent. It only gives an excuse for lazy programmers to avoid them.
    I'd say that you are trying to provide an excuse for lazy programmers to use such implicit conversions even when they are dangerous.

    Quote Originally Posted by phantomotap
    Inheritance and multiple inheritance, especially the "Curiously Recurring Template Pattern", can interfere with overload resolution in the exact same ways.
    I'd say that in those cases you usually actually want this "interference", so it would be akin to specific examples where implicit conversion is an acceptable trade-off.

    Quote Originally Posted by phantomotap
    Actually, the implicit conversion provided on the constructor side of things, which 'std::string' provides, can yield similarly unexpected results.
    Indeed, hence a decision to not declare a constructor that can take exactly one argument explicit needs to be carefully considered.

    Quote Originally Posted by phantomotap
    As for logically illegal or just silly operations--once again: protect your class. Write the firewalls to prevent them from compiling.
    Demonstrate how you would do that to stop Sutter's counter-example and yet provide a conversion function to (const) char*.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Demonstrate how you would do that to stop Sutter's counter-example and yet provide a conversion function to (const) char*.
    ^_^

    This just gets better and better.

    Considering you posted the "Guru of the Week" link I assume you mean the 's1 = s2 - s3;' example.

    The relevant quote from the article: "The subtraction is meaningless and should be wrong. If string had an implicit conversion to const char*, however, this code would compile cleanly because the compiler would silently convert both strings to const char*'s and then subtract those pointers."

    The best part of this is: you are both wrong. First, it isn't a counter. Second, even with the implicit conversion it would not compile for a conforming compiler. Subtracting two pointers yields an integer type and 'std::string' has no such constructor.

    However, I think your point was probably: how do I prevent the silent conversion to 'const char *' from causing problems when used with the subtraction operator. The answer is the same for every operator associated with the intended conversion: provide the operator and either prevent successful compilation or crash with a well-formed error during execution.

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Can anybody show me why this works??
    By tzuch in forum C Programming
    Replies: 2
    Last Post: 03-29-2008, 09:03 AM
  2. Understanding formulas, shapes, algorithms etc
    By hardi in forum A Brief History of Cprogramming.com
    Replies: 26
    Last Post: 04-16-2007, 01:23 PM
  3. Works outside of the loop, not within
    By Decrypt in forum C++ Programming
    Replies: 5
    Last Post: 08-05-2006, 12:22 AM
  4. understanding recursive functions
    By houler in forum C Programming
    Replies: 7
    Last Post: 12-09-2004, 12:56 PM
  5. Understanding Headers and Inclusions
    By jdm in forum C++ Programming
    Replies: 11
    Last Post: 04-21-2004, 10:11 PM

Tags for this Thread