1

I am currently writing my own String class. Here is the header

#pragma once

class String
{
private:
    char* m_Beginning;
    int m_Length;

public:
    // Constructors
    String();
    String(const String&);
    String(const char*);

    // Destructor
    ~String();

    // Operations
    String& operator=(const String&);
    String operator+(const String&)const;
    String& operator+=(const String&);
    char operator[](int _Index)const;

    // Methods
    void Append(const String&);
    String Concatenate(const String&)const;
    int Length()const { return m_Length; };
    void Clear();
};

And here is the class description

#include "String.h"
#include <cstring>
#include <iostream>

String::String()
{
    m_Beginning = nullptr;
    m_Length = 0;
}

String::String(const String& _String)
{
    m_Length = _String.Length();
    m_Beginning = new char[m_Length];
    for (int i = 0; i < m_Length; ++i)
    {
        m_Beginning[i] = _String[i];
    }
}

String::String(const char* _String)
{
    m_Length = strlen(_String);
    m_Beginning = new char[m_Length];
    for (int i = 0; i < m_Length; ++i)
    {
        m_Beginning[i] = _String[i];
    }
}

String::~String()
{
    delete[] m_Beginning;
}

String& String::operator=(const String& _String)
{
    Clear();
    m_Length = _String.Length();
    m_Beginning = new char[m_Length];
    for (int i = 0; i < m_Length; ++i)
    {
        m_Beginning[i] = _String[i];
    }
    return *this;
}

String String::operator+(const String& _String)const
{
    String NewString(*this);
    NewString += _String;
    return NewString;
}

String& String::operator+=(const String& _String)
{
    for (int i = 0; i < _String.Length(); ++i)
    {
        m_Beginning[m_Length + i] = _String[i];
    }
    m_Length += _String.Length();
    return *this;
}

char String::operator[](int _Index)const
{
    return m_Beginning[_Index];
}


void String::Append(const String& _String)
{
    *this += _String;
}

String String::Concatenate(const String& _String) const
{
    return (*this + _String);
}

void String::Clear()
{
    delete[] m_Beginning;
    m_Beginning = nullptr;
    m_Length = 0;
}

The thing I want to ask is how to override the operator [] so that I set a value to a certain cell, not just extract it.

str("ABCD");
str[2] = 'Z'; // and str will become "ABZD".

Thank you :)

chema989
  • 3,962
  • 2
  • 20
  • 33
  • Side-note: If you're writing your own class with operator overloads, I'd strongly recommend reading [Operator overloading](https://stackoverflow.com/questions/4421706/operator-overloading) to get this stuff idiomatically correct (and avoid repeating yourself constantly). Similarly, for a resource managing class, you'll want to read [What is the copy-and-swap idiom?](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom) for the same correctness/simplification of your constructors and assignment operations. – ShadowRanger Apr 24 '17 at 22:37
  • All names that start with underscore and continue with more underscores or a capital letter are reserved for the implementation in any scope. Your program is ill-formed. – Edward Strange Apr 24 '17 at 22:40
  • Thank you for the information. I will have it in mind :) –  Apr 24 '17 at 22:44
  • Hint: you can check out the declarations for the STL `std::basic_string` at http://en.cppreference.com/w/cpp/string/basic_string to re-use the prototypes and write your own implementation. – Davislor Apr 25 '17 at 07:34

1 Answers1

2

You need to return reference to the character within string's internal buffer. You also need to have const and non const versions of operator[] for your string.

In your String header:

const char& String::operator[](int _Index) const;
char& String::operator[](int _Index);

and in your .cpp file:

const char& String::operator[](int _Index) const
{
    return m_Beginning[_Index];
}

char& String::operator[](int _Index)
{
    return m_Beginning[_Index];
}

When you try to assign a character in your string using code like this:

str[2] = 'Z'; // and str will become "ABZD".

compiler will effectively generate code that is identical to this code:

{
  char& ch = str.m_Beginning[2];
  ch = 'Z';
}

Also, for the string case it's probably better to make these methods inline in header file. And another point, usually it's better to put an assert there to verify that index isn't out of bounds and that m_Beginning was allocated:

const char& operator[](int _Index) const
{
    assert(m_Beginning && _Index>=0 && _Index<m_Length);
    return m_Beginning[_Index];
}

char& operator[](int _Index)
{
    assert(m_Beginning && _Index>=0 && _Index<m_Length);
    return m_Beginning[_Index];
}
Pavel P
  • 15,789
  • 11
  • 79
  • 128
  • Thanks it worked! Could you explain why it worked? I would be very grateful. –  Apr 24 '17 at 22:33
  • A one-line return will most likely get inlined by the compiler anyways. IMO, especially when beginning, keep your code together in the cpp file. – Donnie Apr 24 '17 at 22:35
  • @Donnie no, if it's in .cpp nothing will be inlined by compiler. Only linker can do that. MS compiler will inline this one only with LTCG enabled (link time code generation). – Pavel P Apr 24 '17 at 22:37
  • @Pavel: Well, it can be inlined at the link stage if link time optimization is in play, but you'd need to make sure you're actually using the switches to enable LTO. – ShadowRanger Apr 24 '17 at 22:38
  • @pavel - Since you don't believe me, I will provider an older discussion : http://stackoverflow.com/questions/1443982/when-do-compilers-inline-c-code – Donnie Apr 24 '17 at 22:40
  • @Donnie Perhaps you misunderstand that discussion. Compiler cannot inline code that it doesn't see, and outside String.cpp it won't be able to see and inline these methods. – Pavel P Apr 24 '17 at 22:44
  • @NeilButterworth what exactly are you referring to? – Pavel P Apr 24 '17 at 22:45
  • @Donnie regarding that older discussion: I totally agree with accepted answer there, yet, what I say does not contradict with that thread as well – Pavel P Apr 24 '17 at 22:47
  • @Donnie This is more correct regarding meaning of the `inline` keyword and what compiler can and cannot inline check http://stackoverflow.com/a/1445592/468725 "If the function is not defined in the header, the compiler only has the declaration and cannot inline the function even if it wanted to." – Pavel P Apr 24 '17 at 22:54
  • @ShadowRanger exactly, that's why I say, LTO or LTCG in ms compilers, otherwise it cannot be inlined. – Pavel P Apr 24 '17 at 22:57
  • @Pavel: It is not only Microsoft compilers that can perform link-time optimisation. Though I would also implement this whole thing in a header. – Lightness Races in Orbit Apr 25 '17 at 00:22
  • @BoundaryImposition As mentioned LTO in [gcc](https://gcc.gnu.org/wiki/LinkTimeOptimization) or [clang](http://llvm.org/docs/LinkTimeOptimization.html) and LTCG in [ms compilers](https://msdn.microsoft.com/en-us/library/xbf3tbeh.aspx). Though I find all of them aren't much practical in large projects. For that reason there is thinLTO and new advanced LTCG options in ms compilers as well. – Pavel P Apr 25 '17 at 01:08