2

Following is my code: I dont understand why the move constructor does not get invoked.

Mystring.h:

#ifndef _MYSTRING_H
#define _MYSTRING_H

#include<iostream>
#include<cstring>

#endif

class Mystring{
private:
    char * str;
public:
    Mystring();                         //No arg constructor
    Mystring(char *str);                //Overloaded constructor
    Mystring(const Mystring &source);   //Copy constructor
    Mystring(Mystring &&source);        //Move constructor

    //operator overloading
    bool operator==(const Mystring &rhs);
    bool operator!=(const Mystring &rhs);
    bool operator<(const Mystring &rhs);
    bool operator>(const Mystring &rhs);
    Mystring &operator+(const Mystring &rhs);
    };

Mystring.cpp:

#include"mystring.h"


Mystring::Mystring():str{nullptr}{
        std::cout<<"No args constructor"<<std::endl;
        }

Mystring::Mystring(char *str){
    this->str = nullptr;
    this->str = new char[strlen(str)+1];
    strcpy(this->str,str);
    std::cout<<"Overloaded constructor called"<<std::endl;
    }

Mystring::Mystring(const Mystring &source){
            delete[] this->str;
            this->str = nullptr;
            this->str = new char[strlen(source.str) + 1];
            strcpy(this->str,source.str);
            std::cout<<"copy constructor called"<<std::endl;
        }

Mystring::Mystring(Mystring &&source){
        this->str  = source.str;
        source.str = nullptr;
        std::cout<<"Move constructor called"<<std::endl;
        }

bool Mystring::operator==(const Mystring &rhs){
    std::cout<<"operator == called"<<std::endl;
    return strcmp(this->str,rhs.str);

    }

bool Mystring::operator!=(const Mystring &rhs){
    std::cout<<"operator != called"<<std::endl;
    return !(operator==(rhs));

    }

bool Mystring::operator <(const Mystring &rhs){
        std::cout<<"operator < called"<<std::endl;
         if(strcmp(this->str,rhs.str) < 0){
             return 0;
             }
         else 
             return 1;
    }

bool Mystring::operator >(const Mystring &rhs){
        std::cout<<"operator > called"<<std::endl;
        return(!(operator<(rhs)));
    }

Mystring & Mystring::operator+(const Mystring &rhs){
    std::cout<<"operator+ called"<<std::endl;
    char *temp = new char[strlen(this->str) + strlen(rhs.str)+ 1];
    strcat(temp,this->str);
    strcat(temp,rhs.str);

    }

main.cpp:

#include"Mystring.h"


int main(){
    Mystring A {"cat"}; //Overloaded constructor
    Mystring B {"dog"}; //Overloaded constructor
    Mystring C {A};     //Copy constructor
    Mystring D {Mystring{"Hello"}};  //Overloaded and then move constructor
    return 0;
    }

Mydoubts:

The statement Mystring D {Mystring{"Hello"}}; never invokes move constructor. I don't understand why Mystring {"Hello"} doesn't generate any temporary object following which move constructor should be called.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • What result did you get? – songyuanyao Nov 18 '19 at 02:10
  • It just invokes the overloaded constructor and C does gets the string "Hello". But I dont understand why it fails to invoke the move constructor – Vikrant Waje Nov 18 '19 at 02:12
  • `#define _MYSTRING_H` That identifier is reserved. By defining it, your program will have undefined behaviour. You should use another header guard. – eerorika Nov 18 '19 at 02:18
  • 2
    @VikrantWaje `Mystring::Mystring(const Mystring &source){ delete[] this->str;` -- Undefined behavior, since `str` is not initialized. Why are you issuing `delete` calls in the copy constructor? The copy constructor is supposed to create a *brand new* object. In addition, `operator +` should be returning a new object, not a reference to an existing object. – PaulMcKenzie Nov 18 '19 at 02:23
  • 1
    "Overloaded" constructor should take const char pointer: `Mystring(const char* str);` – 4LegsDrivenCat Nov 18 '19 at 03:43
  • 1
    @VikrantWaje In addition to what PaulMcKenzie said about `operator+`, you should use `strcpy` instead of first `strcat` because newly allocated memory is not zero-initialized and may have garbage. And don't forget to add `return temp;`. – 4LegsDrivenCat Nov 18 '19 at 03:51

1 Answers1

3

The statement Mystring D {Mystring{"Hello"}}; never invokes move constructor

Because of copy elision; which is guaranteed since C++17 for this case.

When an object is initialized from a prvalue of the same type (ignoring cv-qualification), it's initialized directly from the initializer. No temporary is created, and no copy or move is made. This is sometimes called "guaranteed copy elision", though unlike copy elision, it's mandatory and is not an optimization.

That means given Mystring D {Mystring{"Hello"}};, D is initialized from "Hello" directly. No temporary, no copy / move operation.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Even if I use Mystring C = B + A; the program crashes, why is such behaviour observed then. the operator+ gets invoked successfully but in the copy constructor, the program crashes at strlen() function. Any idea about this error? – Vikrant Waje Nov 18 '19 at 02:26
  • 1
    @VikrantWaje The program crashes because of other errors, some pointed out in my comments in the main section. – PaulMcKenzie Nov 18 '19 at 02:27
  • 1
    @VikrantWaje That's another issue; you can get some hints from the comments. – songyuanyao Nov 18 '19 at 02:28
  • Figured it out. Thanks@PaulMcKenzie Thanks @songyuanyao – Vikrant Waje Nov 18 '19 at 02:31