#include <iostream>
#include <cassert>
using namespace std;

class MyString
{
//private:
public:
    char *m_data = nullptr;
    int m_length = 0;
public:
    MyString(const char *source = "")
    {
        assert(source);
        
        m_length = std::strlen(source) + 1; // 문자열의 마지막이라는 것을 뜻하게 위한 nullptr을 위한 자리
        m_data = new char[m_length];
        
        for (int i=0; i < m_length; ++i)
        	m_data[i] = source[i];
        
        m_data[m_length - 1] = '\0';
    }
    
    ~MyString()
    {
        delete[] m_data;
    }
    
    char* getString() { return m_data; }
    int getLength() { return m_length; }
};

int main()
{
    MyString hello("Hello");
    
    cout << (int*)hello.m_data << "\n"; // 문자열로 인식하기 때문에 인트형으로 변환
    cout << hello.getString() << "\n";
    
    {
        MyString copy = hello; // 기본적으로 들어있는 복사 생성자는 값만 복사하기 때문에 주소도 복사해준다.(얕은 복사) new로 할당된 메모리가 없다는 
        						// 뜻! 근데 이 scope를 벗어나면 destructor로 인해 delete됨
        cout << (int*)copy.m_data << "\n";
        cout << copy.getString() << "\n";
    }
    
    cout << hello.getString() << "\n"; // copy에서 동적 할당 메모리를 삭제해버렸기 때문에 이상한 값이 떠버리게 된다.
    
    return 0;
}
#include <iostream>
#include <cassert>
using namespace std;

class MyString
{
//private:
public:
    char *m_data = nullptr;
    int m_length = 0;
public:
    MyString(const char *source = "")
    {
        assert(source);
        
        m_length = std::strlen(source) + 1; 
        m_data = new char[m_length];
        
        for (int i=0; i < m_length; ++i)
        	m_data[i] = source[i];
        
        m_data[m_length - 1] = '\0';
    }
 
    // MyString (const MyString &source) = delete; // copy constructor를 만들 여력이 없을 때 얕은 복사를 막기 위해 사용. 하지만 그냥 차선														책일뿐
    
    MyString( const MyString &source)
    {
        cout << "Copy constructor " << "\n";
        
        m_length = source.m_length;
        
        if (source.m_data != nullptr)
        {
            m_data = new char[m_length]; // 주소를 다시 할당하고 다시 복사 => 깊은 복사(주소를 복사하는 것이 아니라 내용을 복사)
            
            for (int i=0; i < m_length; ++i)
                m_data[i] = source.m_data[i];
        }
        else
            m_data = nullptr;
    }
    
    MyString& operator = (const MyString & source) // 대입 연산자
    {
        // shallow copy 라면
        //this->m_data = source.m_data;
        //this->m_length = source.m_length;
        // 이런식으로 값만 할당. 이것이 기본 constructor
        
        cout << "Assignment operator " << "\n";
        
        if (this == &source) // prevent self-assignment 아무것도 하지 못하게 막는다.
            return *this
        
        delete[] m_data;
        
        m_length = source.m_length;
        
        if (source.m_data != nullptr)
        {
            m_data = new char[m_length];
            
            for (int i=0; i < m_length; ++i)
                m_data[i] = source.m_data[i];
        }
        else
            m_data = nullptr;
    }
    
    
    
    ~MyString()
    {
        delete[] m_data;
    }
    
    char* getString() { return m_data; }
    int getLength() { return m_length; }
};

int main()
{
    MyString hello("Hello");
    
    //cout << (int*)hello.m_data << "\n"; 
    //cout << hello.getString() << "\n";
    
    //{
    //    MyString copy = hello; 
    //    cout << (int*)copy.m_data << "\n";
    //    cout << copy.getString() << "\n";
    //}
    
    //cout << hello.getString() << "\n"; // copy와 hello가 가지고 있는 주소가 다르기 때문에 제대로된 값이 나온다.
    
    // MyString str1(hello); // MyString str1 = hello; 와 기능상 같아서 copy constructor라는 것을 알 수 있다.
    
    MyString str1 = hello; // copy constructor. 최초로 만들어지는 순간이기 때문
    
    MyString str2; // 그냥 생성자.
    str2 = hello; // assignment constructor. 
    
    
    
    
    
    
    
    return 0;
}