Overload operator[] for Char assignment - C++ - c++

I am fairly new to C++, although I do have some experience programming. I have built a Text class that uses a dynamic char* as it's main member. The class definition is below.
#include <iostream>
#include <cstring>
using namespace std;
class Text
{
public:
Text();
Text(const char*); // Type cast char* to Text obj
Text(const Text&); // Copy constructor
~Text();
// Overloaded operators
Text& operator=(const Text&);
Text operator+(const Text&) const; // Concat
bool operator==(const Text&) const;
char operator[](const size_t&) const; // Retrieve char at
friend ostream& operator<<(ostream&, const Text&);
void get_input(istream&); // User input
private:
int length;
char* str;
};
The issue I am having is I don't know how to use operator[] to assign a char value at the given index that's passed in. The current overloaded operator operator[] is being used to return the char at the index supplied. Anyone have experience with this?
I would like to be able to do something similar to:
int main()
{
Text example = "Batman";
example[2] = 'd';
cout << example << endl;
return 0;
}
Any help and/or advice is appreciated!
Solution provided - Thanks a bunch for all the replies
char& operator[](size_t&); works

You need to provide a reference to the character.
#include <iostream>
struct Foo {
char m_array[64];
char& operator[](size_t index) { return m_array[index]; }
char operator[](size_t index) const { return m_array[index]; }
};
int main() {
Foo foo;
foo[0] = 'H';
foo[1] = 'i';
foo[2] = 0;
std::cout << foo[0] << ", " << foo.m_array << '\n';
return 0;
}
http://ideone.com/srBurV
Note that size_t is unsigned, because negative indexes are never good.

This article is the definitive guide to operator overloading in C++ (which, to be honest, is mainly boilerplate code for syntactic sugar). It explains everything that is possible:
Operator overloading
Here's the portion that is of interest to you:
class X {
value_type& operator[](index_type idx);
const value_type& operator[](index_type idx) const;
// ...
};
And yes, this is possible, for the many of the STL containers (the vector for example), allow for array subscript notation to access data.
So you can do something along the lines of this:
char & operator[]( size_t i )
{
return *(str + i);
}

You should overload operator[] as non const method and return a reference from it
char& operator[](const int&);

Related

C++ operator overloading [], where is the data parameter to assign?

I want to add a overload the operator [] in my class. Operator overloading is not something I've had to do before.
I want to write an implementation to do the following:
myclass a;
a["test"] = 123;
int test = a["test"];
So far in my class the prototype looks like this:
string operator[](const char* cpszLabel);
The implementation isn't complete and looks like this:
string myclass::operator[](const char* cpszLabel) {
string strContent;
if ( cpszLabel != nullptr ) {
}
return strContent;
}
What I'm not sure about is how to reference the data that is being assigned or does this require overloading the '=' too?
I've added an overload for the '=' operator, but this doesn't get called:
Prototype:
string operator=(int intData);
Implementation:
string myclass::operator=(int intData) {
char szString[24];
sprintf(szString, "\"%d\"", intData);
return string(szString);
}
You need to arrange things so operator[](const char* cpszLabel) returns a reference to something in your class.
int& operator[](const char* cpszLabel);
is probably a better prototype.
You can then modify that "something" in your class via that reference. To be honest though what you want can be achieved with
typedef std::map<std::string, int> myclass;
and most folk don't bother with the typedef, especially now that we have auto. If you want to use a std::map as a member variable in the class (in order to reduce functionality &c.), then the following is a starting point:
class myclass
{
std::map<std::string, int> m_data;
public:
int& operator[](const char* cpszLabel)
{
return m_data[cpszLabel];
}
};
In a["test"] = 123;, the "receiver" of the assignment is the object that's returned from the lookup, which is a string.
You can't overload string's assignment operator.
But, as is well known, every problem can be solved by introducing a level of indirection.
You can store a type of your own instead of std::string, and let that handle the conversion.
A very small example as illustration:
struct Data
{
template<typename T>
Data& operator=(const T& rhs)
{
std::ostringstream os;
os << rhs;
value = os.str();
return *this;
}
operator const char*() const { return value.c_str(); }
std::string value;
};
struct Container
{
Data& operator[] (const std::string& s) { return table[s]; }
std::map<std::string, Data> table;
};
int main()
{
Container cont;
cont["foo"] = "bar";
cont["baz"] = 123;
cont["goo"] = 5.5;
for (auto v: cont.table)
{
std::cout << v.first << " --> " << v.second << '\n';
}
}
Output:
baz --> 123
foo --> bar
goo --> 5.5

Overloading the operator=? c++

I having a trouble trying to insert an object from a class that I created to a char. I create a class name Element as part of a bigger program, but when I try to overloading operator= so that i can get in char variable the char I got in the Element obj nothing work...
Element.h
class Element {
private:
char val;
public:
Element();
Element(char newVal); //check input - throw execption
~Element();
friend ostream& operator<<(ostream& os, const Element& obj);
void setElement(char newVal);
char getElement();
void operator= (const Element newVal);
void operator= (char newVal);
};
Element.cpp
#include "Element.h"
Element::Element()
{
val ='.';
}
Element::Element(char newVal)
{
if (newVal!='X' && newVal!='O'&& newVal!='.'){
inputExecption error;
error.what();
} else{
val=newVal;
}
}
Element::~Element()
{
}
void Element::setElement(char newVal)
{
if (newVal!='X' && newVal!='O'&& newVal!='.'){
inputExecption error;
error.what();
} else{
val=newVal;
}
}
char Element::getElement()
{
return val;
}
ostream& operator<<(ostream& os, const Element& obj)
{
return os << obj.val;
}
void Element::operator= (const Element Value){
val = Value.val;
}
void Element::operator= (char newVal){
if(val != 'X' && val != 'O'){
inputExecption a;
a.what();
}
val = newVal;
}
So as I said, what i'm trying to do is:
Element x='X';
char c = x; //(c=x.val)
cout<<c<<endl;//Will print X
thx! :)
In other words you're trying to convert an object of type Element to an object of type char. You can use a conversion operator for this:
class Element {
// ...
operator char() const { return val; }
};
The assignment operator only works when an instance of Element is used on the left hand side of the assignment.
IMHO, writing an implicit conversion operator (as 0x499602D2 suggested) is not the best idea.
For example, assume you have two separate file something like this:
//File char_utils.hh
#include <iostream>
void accept(char character) { std::cout <<"character: " <<character <<'\n'; }
//File elements_utils.hh
#include <iostream>
void accept(Element const& element) {
std::cout <<"element.getVal(): " <<element.getVal() <<'\n';
}
Then, depending on what you include in your implementation file (char_utils.hh or elements_utils.hh) you will get different behaviour, which may lead to many subtle bugs and/or interesting behaviours. ;)
This can be overcome by declaring conversion operator explicit, i.e.,
explicit operator char() const { return val; }
In such a case, you just need to:
Element e{'x'};
char c = static_cast<char>(e);
which shows the intent more clearly.
However, why do you need using an implicit conversion at all? Especially when you can use Element::getElement()? And if I may suggest, I would use different namin, e.g., Element::getValue(). Element::getElement() is very confusing.
Further reading: https://stackoverflow.com/a/16615725/1877420

std::map key not found even though the entries are identical

I'm learning C++ and I've been writing a wrapper for std::map and std::string, and I've stumbled upon a problem. Whenever I add something to the map using a string as key, once I try to access that item using the exact same key it says the key is out of bounds of the map. Here's my code (irrelevant parts left out):
ADictionary.h
#ifndef ADICTIONARY_H
#define ADICTIONARY_H
#include <map>
...
template<typename KEY, typename VALUE>
class ADictionary {
public:
...
VALUE operator [](KEY key) const {
return value.at(key);
}
void add(KEY key, VALUE value) {
this->value.insert(std::make_pair(key, value));
}
...
private:
std::map<KEY, VALUE> value;
};
#endif
AString.cpp
#include "AString.h"
AString::AString() {
value = "";
}
AString::AString(const char character) {
value = character;
}
AString::AString(const char * characters) {
value = characters;
}
AString::AString(std::string text) {
value = text;
}
...
AString::operator const char *() const {
return value.c_str();
}
AString::operator const std::string() const {
return value;
}
...
ABoolean AString::operator<(AString & text) const {
return getLength() < text.getLength();
}
ABoolean AString::operator>(AString & text) const {
return text < *this;
}
ABoolean AString::operator==(AString & text) const {
return value == text.value;
}
ABoolean AString::operator!=(AString & text) const {
return !(text == *this);
}
AString & AString::operator=(AString & text) {
value = text.value;
return *this;
}
...
The code which uses the above
ADictionary<AString, AString> test;
AString a = "a";
AString b = "b";
test.add(a, b);
std::cout << test[a]; // Error occurs here, according to the program "a" is not a key in the map
I hope someone can explain to me what's going wrong. I've tried creating a dictionary with the default std::string as types and it worked correctly:
ADictionary<std::string, std::string> test;
std::string a = "a";
std::string b = "b";
test.add(a, b);
std::cout << test[a]; // No error this time
As I've said, I'm pretty new to C++ so there may be other errors. If so, feel free to point them out.
Thanks!
EDIT:
AString.h
#ifndef ASTRING_H
#define ASTRING_H
#include <string>
#include "ABoolean.h"
#include "AInteger.h"
#include "AList.h"
class ABoolean;
class AInteger;
template<typename VALUE>
class AList;
class AString {
public:
AString();
AString(const char);
AString(const char *);
AString(std::string);
~AString();
operator const char *() const;
operator const std::string() const;
operator const AInteger() const;
ABoolean operator<(AString &) const;
ABoolean operator>(AString &) const;
ABoolean operator==(AString &) const;
ABoolean operator!=(AString &) const;
AString & operator=(AString &);
AString & operator+(AString &);
AString & operator+=(AString &);
void clear();
ABoolean contains(AString) const;
AInteger getIndex(AString) const;
AInteger getLength() const;
AList<AString> getSplit(AString) const;
AString getSubstring(AInteger, AInteger) const;
void removeRange(AInteger, AInteger);
void removeSubstring(AString);
void toLowercase();
void toUppercase();
private:
std::string value;
};
AString & operator+(const char, AString &);
AString & operator+(const char *, AString &);
#endif
Your string operators appear to be incorrect.
std::map uses the less than operator by default. While you provide one for AString, the only thing it does is check the length of the string. What if the two strings are of equal length?
The correct thing to do is to lexicographically compare the characters in the string. While there is a standard library function to do this, you can use operator < of the std::string values in your class:
friend bool operator<(AString const& a, AString const& b)
{
return a.value < b.value;
}
EDIT: You may also wish to remove your conversion operators, or at least make them explicit, which prevents surprising and unwanted implicit conversions. Constructors taking one parameter (other than copy or move constructors) should also be declared explicit.

C++: Non-const operator taking preference over const overload

I have a class that represents a diagonal matrix. I only store the elements along the diagonal, so I don't waste space with a bunch of 0's. However, I still want to be able to use double brackets to access elements in the array. To get around that, I use an inner class, like this:
template <class T>
class DiagonalMatrix
{
private:
const T ZERO = 0;
int _size;
Vector<T> _data;
class row
{
private:
DiagonalMatrix<T>* _parent;
int _row;
public:
row(DiagonalMatrix<T>* parent, const int row)
: _parent(parent), _row(row) {}
T& operator[](const int i);
};
class const_row
{
private:
const DiagonalMatrix<T>* const _parent;
int _row;
public:
const_row(const DiagonalMatrix<T>* const parent, const int row)
: _parent(parent), _row(row) {}
const T& operator[](const int i) const;
};
friend class row;
friend class const_row;
public:
row operator[] (const int i);
const const_row operator[] (const int i) const;
// other stuff
};
And here are the relevant definitions:
template<class T>
typename DiagonalMatrix<T>::row DiagonalMatrix<T>::operator[](const int i)
{
if (i < 0 || i >= _size)
{
throw IndexOutOfBoundsException(i);
}
return DiagonalMatrix<T>::row(this, i);
}
template <class T>
T& DiagonalMatrix<T>::row::operator[](const int i)
{
if (i < 0 || i >= _parent->_size)
{
throw IndexOutOfBoundsException(i);
}
if (row == col)
{
return _parent->_data[row];
}
// TODO Add a real exception
throw "Cannot modify non-diagonal elements";
}
With similar definitions for the const versions, except the const operator[] returns a reference to the constant ZERO instead of throwing for non-diagonal elements.
So here is my problem: The non-const version is being called even when I don't need to modify anything. For example, this throws my error string:
DiagonalMatrix<double> diag(5);
// fill in the diagonal elements with some values
cout << diag[0][2] << endl;
However, if I remove the non-const versions of the operator, it behaves as expected and outputs a 0.
I've also tried something like:
T& at(const int row, const int col);
const T& at(const int row, const int col) const;
// in main:
cout << diag.at(0, 2) << endl;
But this has the same issue. So I have two questions:
1) Why does C++ choose the non-const version of the function over the const version even when I am not assigning to the result? Doesn't operator<< typically pass the right-hand object by const&?
2) How can I get around this? I'd rather not have separate get() and set() functions if I can help it.
1) Why does C++ choose the non-const version of the function over the const version even when I am not assigning to the result? Doesn't operator<< typically pass the right-hand object by const&?
It choose the non-const version because diag is not a const object.
2) How can I get around this? I'd rather not have separate get() and set() functions if I can help it.
You can define a const refrence to diag and use it to print values:
const DiagonalMatrix<double> &const_diag = diag;
cout << diag[0][2] << endl; // call const version
Or you define a function to print values and pass a const reference in:
void show_diag(const DiagonalMatrix<double> &diag)
{
cout << diag[0][2] << endl; // call const version
}
show_diag(diag);

Overloading operator []

Let's say I have a container class called MyContainerClass that holds integers.
The [] operator, as you know, can be overloaded so the user can more intuitively access values as if the container were a regular array. For example:
MyContainerClass MyInstance;
// ...
int ValueAtIndex = MyInstance[3]; // Gets the value at the index of 3.
The obvious return type for operator[] would be int, but then the user wouldn't be able to do something like this:
MyContainerClass MyInstance;
MyInstance[3] = 5;
So, what should the return type for operator[] be?
The obvious return type is int& :)
For increased elaboration:
int &operator[](ptrdiff_t i) { return myarray[i]; }
int const& operator[](ptrdiff_t i) const { return myarray[i]; }
// ^ could be "int" too. Doesn't matter for a simple type as "int".
This should be a reference:
int &
class MyContainerClass {
public:
int& operator[](unsigned int index);
int operator[](unsigned int index) const;
// ...
};
Returning a reference lets the user use the result as an lvalue, as in your example MyInstance[3] = 5;. Adding a const overload makes sure they can't do that if MyInstance is a const variable or reference.
But sometimes you want things to look like that but don't really have an int you can take a reference to. Or maybe you want to allow multiple types on the right-hand side of MyInstance[3] = expr;. In this case, you can use a dummy object which overloads assignment:
class MyContainerClass {
private:
class Index {
public:
Index& operator=(int val);
Index& operator=(const string& val);
private:
Index(MyContainerClass& cont, unsigned int ind);
MyContainerClass& m_cont;
unsigned int m_ind;
friend class MyContainerClass;
};
public:
Index operator[](unsigned int ind) { return Index(*this, ind); }
int operator[](unsigned int ind) const;
// ...
};
int&
returning a reference allows you too use the returned value as a left-hand side of the assignment.
same reason why operator<<() returns an ostream&, which allows you to write cout << a << b;