How Does the String Class Override the + Operator

How does the String class override the + operator?

Let's look at the following simple expressions in Java

int x=15;
String temp="x = "+x;

The compiler converts "x = "+x; into a StringBuilder internally and uses .append(int) to "add" the integer to the string.

5.1.11. String Conversion

Any type may be converted to type String by string conversion.

A value x of primitive type T is first converted to a reference value
as if by giving it as an argument to an appropriate class instance
creation expression (§15.9):

  • If T is boolean, then use new Boolean(x).
  • If T is char, then use new Character(x).
  • If T is byte, short, or int, then use new Integer(x).
  • If T is long, then use new Long(x).
  • If T is float, then use new Float(x).
  • If T is double, then use new Double(x).

This reference value is then converted to type String by string
conversion.

Now only reference values need to be considered:

  • If the reference is null, it is converted to the string "null" (four ASCII characters n, u, l, l).
  • Otherwise, the conversion is performed as if by an invocation of the toString method of the referenced object with no arguments; but
    if the result of invoking the toString method is null, then the
    string "null" is used instead.

The toString method is defined by the primordial class Object
(§4.3.2). Many classes override it, notably Boolean, Character,
Integer, Long, Float, Double, and String.

See §5.4 for details of the string conversion context.

15.18.1.

Optimization of String Concatenation :
An implementation may choose to perform conversion and concatenation
in one step to avoid creating and then discarding an intermediate
String object. To increase the performance of repeated string
concatenation, a Java compiler may use the StringBuffer class or a
similar technique to reduce the number of intermediate String objects
that are created by evaluation of an expression.

For primitive types, an implementation may also optimize away the
creation of a wrapper object by converting directly from a primitive
type to a string.

The optimized version will not actually do a full wrapped String conversion first.

This is a good illustration of an optimized version used by the compiler, albeit without the conversion of a primitive, where you can see the compiler changing things into a StringBuilder in the background:

http://caprazzi.net/posts/java-bytecode-string-concatenation-and-stringbuilder/


This java code:

public static void main(String[] args) {
String cip = "cip";
String ciop = "ciop";
String plus = cip + ciop;
String build = new StringBuilder(cip).append(ciop).toString();
}

Generates this - see how the two concatenation styles lead to the very same bytecode:

 L0
LINENUMBER 23 L0
LDC "cip"
ASTORE 1
L1
LINENUMBER 24 L1
LDC "ciop"
ASTORE 2

// cip + ciop

L2
LINENUMBER 25 L2

NEW java/lang/StringBuilder
DUP
ALOAD 1
INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

ASTORE 3

// new StringBuilder(cip).append(ciop).toString()

L3
LINENUMBER 26 L3

NEW java/lang/StringBuilder
DUP
ALOAD 1
INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

ASTORE 4
L4
LINENUMBER 27 L4
RETURN

Looking at the example above and how the byte code based on the source code in the given example is generated, you will be able to notice that the compiler has internally transformed the following statement

cip+ciop; 

into

new StringBuilder(cip).append(ciop).toString();

In other words, the operator + in string concatenation is effectively a shorthand for the more verbose StringBuilder idiom.

Overriding operator for a Strings class

You don't seem to be doing anything with the character you get from the stream

istream& operator>>(istream &is, String &s) {
char data[ String::BUFF_INC ];
int idx = 0;
data[ 0 ] = is.get();
while( (data[ idx ] != *String::WHITESPACE) && !is.ios::fail() ) {
++idx;
is.get(); // you don't do anything with this
data[ idx ] = s[ idx ]; // you're copying the string into the buffer
}
return is;
}

So it checks whether the string s contains a whitespace, not whether you read a whitespace from the stream.

How do I overload the == operator for the string class in c++?

Well, I need to make several points here.

  • If by string you mean char arrays/pointers, then you cannot overload operator ==, since operator overloading is allowed only for user-defined types

  • If by strings you mean std::string, then you can't overload operator == either, since it is already overloaded :)

  • In order to do case-insensitive comparison, the best approach is to have a named function such as case_insensitive_equal. Boost has one - boost::iequals(str1, str2)

  • You could attempt to write your own char_traits to create a case insensitive string type

As to how to write a function comparing strings in case insensitive manner, I'd do this:

bool case_insensitive_equal(const std::string& s1, const std::string& s2)
{
if(s1.length() != s2. length())
return false;
for(int i = 0; i < s1.length(); ++i)
if(std::toupper(s1[i]) != std::toupper(s2[i])) //tolower would do as well
return false;
return true;
}

Instead of loops you could use std::transform and std::equal,but I think this is more efficient.

Overloading + operator in String class

The problem is that you are using strcpy() and strcat() to populate the old allocation, which is not large enough. Therefore, the strcat() invocation attempts to write past the end of this allocation and the result is undefined behavior, which is manifesting as a crash on your system.

char* temp = value;
temp = new char[strlen(value) + s.length() +1];
strcpy(value, temp); // <-- should be copying the other way
strcat(value, s.getValue()); // <-- should be copying into temp, not value
//if (size != 0) {
// delete[]temp;
//}
size = strlen(value);
return *this;

Rewritten to solve this problem, as well as release the original string, and check for value == nullptr:

char *oldString = value == nullptr ? "" : value;

int newSize = strlen(oldString) + s.length();
char *newString = new char[newSize + 1];

strcpy(newString, oldString);
strcat(newString, s.getValue());

delete [] value;

value = newString;
size = newSize;

return *this;

how to overload the == operator for strings?

You can't override operators for pre-existing classes. The closest you can get is to make an extension method:

public static bool EqualsCaseInsensitive(this String a, String b) {
return String.Equals(a, b, StringComparison.OrdinalIgnoreCase);
}

You can use it like so:

var areSame = stringA.EqualsCaseInsensitive(stringB);

That being said, it's considered bad practice to add extension methods to core types like String. You'd be better off just having a utility method do the comparison instead. And in this particular case, the utility method you need already exists:

var areSame = String.Equals(stringA, stringB, StringComparison.OrdinalIgnoreCase);

Overloading operator +. String class

Your operator has bugs!

S temp;
//^^^^ has only one byte buffer!!!
temp.string = strcat(first.strS(),second.strS());
// 1 byte ^^^^^ strcat appends second.strS to first.strS

You should re-allocate memory for temp:

S temp;
temp.l = first.lenght() + second.lenght();
delete [] temp.string; // !!!! -
temp.string = new char[temp.l + 1]; // !!!!
// you should have another c-tor which can allocate memory!!!
// like: S(unsigned length, unsigned char c = '\0')
strcpy(temp.string, first.strS());
strcat(temp.string, second.strS());

Besides this obvious bug - you should also take care of exceptions - std::bad_alloc for example. Look at copy-and-swap idiom for better approach for this task.

inherit from std::string or operator overloading

You can provide a user-defined conversion operator to std::string:

class Test {
//...
public:
operator std::string () const {
return /*something*/;
}
};

This will allow a Test object to be implicitly-converted to a std::string.

What makes the String class in Kotlin to be able to work with square brackets?

You can use Kotlin's operator overloading, specifically the overloading for indexed access operators.

The syntax "Hey"[1] is just an alias for "Hey".get(1) (if not on the left-hand side of an assignment, where it would be an alias for "Hey".set(1, ...)).



Related Topics



Leave a reply



Submit