CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 11 of 11
  1. #1
    Join Date
    Jan 2009
    Location
    England
    Posts
    57

    [RESOLVED] A simple vmString class

    I am currently trying to eliminate the use of char * within my project in favour of having my own string class.

    I have made notes in the header and source code of the errors. I have several issues/questions I need to resolve. Operator overloading is new to me, but I hope to understand this better as I also want to improve other classes like vmSize and vmPoint.

    The issues/questions are:

    Q1. Why do we need to make a forward declaration of the class ostream?

    Q2. Why can't the overloaded << operator access the private data member char *m_pchStr when it's a friend of the vmString class?

    Q3. Where should the overloaded (friend) << operator go within the class definition, and does it matter?

    Q4. Within the destructor, should I set m_pchStr to NULL or 0 after delete?

    Q5. Can the copy constructor go into an infinite loop? How to prevent?

    Q6. How can I change std::cout << &testStr << std::endl; so that I do not have to proceed the variable with & ?

    Q7. Should I use exception handling within AllocString() instead of assert()?

    Q8. Does a string literal automatically append a null terminator at the end?

    Code:
    #ifndef __VMSTRING_H__
    #define __VMSTRING_H__
    
    #include <iostream>
    
    class ostream;  // Why needed - error: 'ostream' does not name a type
    
    namespace vmStd {
    
    class vmString {
       friend ostream &operator<<(ostream &output, const vmString &str);
    
    public:
       vmString(const char *str = "");
       vmString(const vmString &cpy);
       ~vmString();
    
       int length() const { return m_nLength; }
    
    private:
       void AllocString(const char *str);
    
       int m_nLength;
       char *m_pchStr;  // error: char* vmStd::vmString::m_pchStr' is private
    };
    
    } // namespace vmStd
    
    #endif // __VMSTRING_H__
    Code:
    #include <cstring>
    #include <cassert>
    
    #include "vmString.h"
    
    using namespace vmStd;
    
    //-----------------------------------------------------------------------------
    // FN/Method:     vmString(const char * = "")
    // Description:   vmString class constructor. Converts char * to a vmString
    //                object.
    //-----------------------------------------------------------------------------
    vmString::vmString(const char *str) : m_nLength(strlen(str)), m_pchStr(0)
    {
       AllocString(str);
    }
    
    //-----------------------------------------------------------------------------
    // FN/Method:     vmString(const vmString &)
    // Description:   vmString class copy constructor
    //-----------------------------------------------------------------------------
    vmString::vmString(const vmString &cpy) : m_nLength(cpy.m_nLength)
    {
       AllocString(cpy.m_pchStr);
    }
    
    //-----------------------------------------------------------------------------
    // FN/Method:     ~vmString()
    // Description:   vmString class destructor
    //-----------------------------------------------------------------------------
    vmString::~vmString()
    {
       delete [] m_pchStr;
    }
    
    //-----------------------------------------------------------------------------
    // FN/Method:     AllocString(const char *)
    // Description:   Dynamically allocate memory for new vmString object
    //-----------------------------------------------------------------------------
    void vmString::AllocString(const char *str)
    {
       m_pchStr = new char[m_nLength + 1];  // Allocate storage
       assert(m_pchStr != 0);               // Terminate if memory not allocated
       strcpy(m_pchStr, str);               // Copy literal to vmString object
    }
    
    //-----------------------------------------------------------------------------
    // FN/Method:     ostream &operator<<(ostream &, const vmString &)
    // Description:   Allow the output of a vmString object
    //-----------------------------------------------------------------------------
    ostream &operator<<(ostream &output, const vmString &str)
    {
       output << str.m_pchStr;  // error: within this context ???
    
       return output;  // Enables cascading
    }
    Code:
    #include <iostream>
    
    #include "vmString.h"
    
    using namespace vmStd;
    
    int main()
    {
       // One part construction
       vmString testStr = "Hello string";
       std::cout << &testStr << std::endl;
    
       return 0;
    }
    Last edited by Gerald Bates; January 13th, 2015 at 05:40 AM.
    What the mind can conceive it can achieve.

  2. #2
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,396

    Re: A simple vmString class

    Quote Originally Posted by Gerald Bates View Post
    I am currently trying to eliminate the use of char * within my project in favour of having my own string class.
    One more attempt to reinvent the wheel?
    Why not just use std::string?
    Victor Nijegorodov

  3. #3
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: A simple vmString class

    Why not just use the standard c++ string class? http://www.cplusplus.com/reference/string/

    You don't forward declare ostream. ostream is part of the namespace std so
    Code:
    friend std::ostream &operator<<(std::ostream &output, const vmString &str);
    and other places where you use ostream.

    You need to define the overloaded operator << within the vmStd namespace.

    This code compiles and produces the expected output
    Code:
    #include <iostream>
    #include <cstring>
    #include <cassert>
    
    namespace vmStd {
    
    	class vmString {
    		friend std::ostream &operator<<(std::ostream &output, const vmString &str);
    
    	public:
    		vmString(const char *str = "");
    		vmString(const vmString &cpy);
    		~vmString();
    
    		int length() const { return m_nLength; }
    
    	private:
    		void AllocString(const char *str);
    
    		int m_nLength;
    		char *m_pchStr;  // error: char* vmStd::vmString::m_pchStr' is private
    	};
    
    } // namespace vmStd
    
    using namespace vmStd;
    
    //-----------------------------------------------------------------------------
    // FN/Method:     vmString(const char * = "")
    // Description:   vmString class constructor. Converts char * to a vmString
    //                object.
    //-----------------------------------------------------------------------------
    vmString::vmString(const char *str) : m_nLength(strlen(str)), m_pchStr(0)
    {
    	AllocString(str);
    }
    
    //-----------------------------------------------------------------------------
    // FN/Method:     vmString(const vmString &)
    // Description:   vmString class copy constructor
    //-----------------------------------------------------------------------------
    vmString::vmString(const vmString& cpy) : m_nLength(cpy.m_nLength)
    {
    	AllocString(cpy.m_pchStr);
    }
    
    //-----------------------------------------------------------------------------
    // FN/Method:     ~vmString()
    // Description:   vmString class destructor
    //-----------------------------------------------------------------------------
    vmString::~vmString()
    {
    	delete[] m_pchStr;
    }
    
    //-----------------------------------------------------------------------------
    // FN/Method:     AllocString(const char *)
    // Description:   Dynamically allocate memory for new vmString object
    //-----------------------------------------------------------------------------
    void vmString::AllocString(const char *str)
    {
    	m_pchStr = new char[m_nLength + 1];  // Allocate storage
    	assert(m_pchStr != 0);               // Terminate if memory not allocated
    	strcpy(m_pchStr, str);               // Copy literal to vmString object
    }
    
    //-----------------------------------------------------------------------------
    // FN/Method:     ostream &operator<<(ostream &, const vmString &)
    // Description:   Allow the output of a vmString object
    //-----------------------------------------------------------------------------
    namespace vmStd {
    	std::ostream &operator<<(std::ostream &output, const vmStd::vmString &str)
    	{
    		output << str.m_pchStr;  // error: within this context ???
    
    		return output;  // Enables cascading
    	}
    }
    
    int main()
    {
    using namespace vmStd;
    
    	// One part construction
    	vmString testStr = "Hello string";
    	std::cout << testStr << std::endl;
    
    	return 0;
    }
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  4. #4
    Join Date
    Jan 2009
    Location
    England
    Posts
    57

    Re: A simple vmString class

    Many thanks VictorN and 2kaud for your replies. It does work now.

    Well to be honest the reason why I wanted to use my own string class was to see if I could eliminate conversion issues when compiling. I have very little knowledge of iostream or making my own string class for that matter.

    I will implement the methods that was going to use the vmString class and use std::string, and see what issues occur.

    Thanks again, your help is appreciated.
    What the mind can conceive it can achieve.

  5. #5
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: A simple vmString class

    Another option may be to use the CString class if you are using Visual Studio (#include "atlstr.h"). It doesn't require MFC and handles UNICODE if that is important to you.

  6. #6
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,234

    Re: A simple vmString class

    Writing a simple string class is not quite simple.
    However, let's try to answer the OP questions.

    Q1. Why do we need to make a forward declaration of the class ostream?
    A1. Once included <iostream>, ostream forward declaration is not necessary; just use std namespace(i.e. std:: ostream).

    Q2. Why can't the overloaded << operator access the private data member char *m_pchStr when it's a friend of the vmString class?
    A2. Your mistake; just put its implementation (as well as all method implementations) in vmStd namespace. Then will can.

    Q3. Where should the overloaded (friend) << operator go within the class definition, and does it matter?
    A3. If you refer to the position inside class definition of a friend function declaration then the answer is "does not matter". The following code, even stupid, is perfectly legal.
    Code:
    class CFoo 
    {
       friend std::ostream &operator<<(std::ostream &output, const CFoo& foo);
       // stuff here...
       friend std::ostream &operator<<(std::ostream &output, const CFoo &foo);
       // other stuff here as well...
       friend std::ostream &operator<<(std::ostream &output, const CFoo &foo);
    };
    Q4. Within the destructor, should I set m_pchStr to NULL or 0 after delete?
    A4. Nope.

    Q5. Can the copy constructor go into an infinite loop? How to prevent?
    A5. It can if you want; otherwise it can not.

    Q6. How can I change std::cout << &testStr << std::endl; so that I do not have to proceed the variable with & ?
    A6. Already changed; see Q/A #2.

    Q7. Should I use exception handling within AllocString() instead of assert()?
    A7. Not obviously. However, just note two things:
    1. the standard new operator throws by default an exception if the allocation fails; see http://www.cplusplus.com/reference/new/operator%20new/
    2. the assert function does not always abort the program; see http://msdn.microsoft.com/en-us/library/9sb57dw4.aspx


    Q8. Does a string literal automatically append a null terminator at the end?
    A8. Append where? If refer to itself, the answer is yes.

    All these being said let's say it again "Writing a simple string class is not quite simple".
    I've just tried to punctually answer the OP questions. Until we can say "we have a simple string class" there is a lot to discuss.
    As already said in this thread, except case is for learning purpose, do not rerinvent the wheel and use an already made string class. I saw tens of homebrew string classes and none was better than widely used ones like (for example) std::string and MFC/ATL CString.
    Last edited by ovidiucucu; January 18th, 2015 at 06:25 AM. Reason: typos
    Ovidiu
    "When in Rome, do as Romans do."
    My latest articles: https://codexpertro.wordpress.com/

  7. #7
    Join Date
    Apr 1999
    Posts
    27,449

    Re: A simple vmString class

    Quote Originally Posted by Gerald Bates View Post
    I am currently trying to eliminate the use of char * within my project in favour of having my own string class.
    One thing that your code lacks that I believe has not been mentioned is that the class lacks an assignment operator. Otherwise, you can't write code like this:
    Code:
    vmString s1="x";
    vmString s2;
    s2 = s1;  // <-- This will cause issues
    A simple implementation of the assignment operator is below:
    Code:
    #include <algorithm>
    //...
    vmString& vmString::operator=(vmString str)
    {
        std::swap(str.m_nLength, m_nLength);
        std::swap(str.m_pchStr, m_pchStr);
        return *this;
    }
    This code works, provided that your copy constructor and destructor for vmString are working correctly. Basically, we allow the compiler to create a copy by using pass-by-value (the str argument). Then the copy's contents are swapped out with the current contents of this.

    Regards,

    Paul McKenzie

  8. #8
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,234

    Re: A simple vmString class

    Quote Originally Posted by Paul McKenzie View Post
    [...]
    A simple implementation of the assignment operator is below:
    Code:
    #include <algorithm>
    //...
    vmString& vmString::operator=(vmString str)
    {
        std::swap(str.m_nLength, m_nLength);
        std::swap(str.m_pchStr, m_pchStr);
        return *this;
    }
    IMHO presenting this solution to someone who still have problems with memory allocation, may easily transform him/her in a copy-coder, code-monkey or script-kiddie.
    Last edited by ovidiucucu; January 23rd, 2015 at 02:29 PM.
    Ovidiu
    "When in Rome, do as Romans do."
    My latest articles: https://codexpertro.wordpress.com/

  9. #9
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: A simple vmString class

    Quote Originally Posted by ovidiucucu View Post
    IMHO presenting this solution to someone who still have problems with memory allocation, may easily transform him/her in a copy-coder, code-monkey or script-kiddie.
    I'm wondering about the whole writing your own string class, but using other parts of the std library.

  10. #10
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,234

    Re: A simple vmString class

    Quote Originally Posted by Arjay View Post
    I'm wondering about the whole writing your own string class, but using other parts of the std library.
    Arjay, no need to wonder (and eventually have nightmares). I never wrote my "own string class, but using other parts of the std library".
    Last edited by ovidiucucu; January 23rd, 2015 at 03:34 PM.
    Ovidiu
    "When in Rome, do as Romans do."
    My latest articles: https://codexpertro.wordpress.com/

  11. #11
    Join Date
    Jan 2009
    Location
    England
    Posts
    57

    Re: A simple vmString class

    Many thanks for your replies. I have indeed taken out the vmString class and used the standard string class instead.
    What the mind can conceive it can achieve.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured