chapter 9-11.
copy = hello; 이렇게 값의 복사를 진행했다면.
default copy constructor로 인해(얉은 복사),
우선은 hello.getstring()이나 copy.getstring()이나 둘다 동일하게 m_data를 출력할 것이다.
그런데 , 우리는 메모리 누수를 방지하기 위해, 생성자에서 new를 해줬다면, 소멸자에서 delete을 해줘야한다 배웠다.
그렇다면 문제가 생긴다.
복사를 했기에, hello객체와 copy 객체는 m_data를 공유한다(정확히 표현하자면, 동일한 주소값을 가지는 m_data)를 가진다.
문자열 자체가 주소값을 return 하기에, 같은 주소값을 공유하는데, 내가 { } 안에서 copy가 사라지면서 delete[]을 했다.
그러면 hello 안에 있던 m_data는? 해제해서 안에 값이 없는 주소를 가리키는 꼴이 되기에 이상한 값이 출력된다.
이 상황은 우리가 원하던 상황이 아니다. 우리가 원하던 상황은? 문자열은 복사가 되지만, 주소값은 공유하지 않길 원한다. 즉 다른 주소값을 가지길 원한다.
이럴 때 복사생성자가 있으면 좋다.
MyString copy = hello; 를 할 때, MyString(const MyString& source), 즉 복사 생성자가 호출되었다.
이럴 경우엔, 주소값을 공유하지 않는다. 왜? 메모리 새로 할당받아서 일일이 넣었으니까.
또 이런 생각을 할 수도 있다. =을 연산자 오버로딩으로 구현하면 되잖아.
그러면, 복사생성자와 = 연산자는 언제일 때 호출이 될까?
연산자 = 오버로딩이나 아까 복사생성자나 코드는 거의 비슷하다.
차이는, 예를 들어 assign하는 값이 서로 같으면(ex. hello = hello)면 문제가 생기니 조건문 하나 달아준거?
그러면 언제 복사 생성자가 호출되고, 언제 연산자가 호출되냐면,
복사 생성자는 당연히 객체가 생성되는 순간에 호출된다.
그러니, 80번째 줄에서 생성하면서 선언하니, 복사 생성자가 호출된다. 연산자가 있음에도 불구하고 말이다.
하지만 84번, 85번줄을 보면 객체를 생성하고, 다시 = 을 이용해 복사해주므로 이럴 땐 = 연산자가 호출되었다.
저번에 class 공부할 때, = 와 ()는 같은 기능을 하는 것이라 배움. 이거 기억하면 헷갈리진 않을 듯.
근데 결론은...!
저거 너무 헷갈리잖아. 그냥 저런 기능 담겨있는 std::string 쓰면 됨 ㅠ.ㅠ..
만약 단순 std::string으론 기능이 부족하다면? 클래스 하나 생성해서 std::string 상속하면 됨. 그리고 더 추가하면 되지.
___________________________________________________________________________________________________________________________
chapter 9-12.
initializer_list라는, 배열의 또 다른 형태인데, 초기화를 더욱 쉽게 해준다.
26번 줄 코드를 보면 위의 다른 배열 초기화 형태와 다르게 단순히 auto 이렇게 썼음을 알 수 있다.
#include <iostream>
#include <cassert>
#include <initializer_list>
using namespace std;
class IntArray
{
private:
unsigned m_length = 0;
int* m_data = nullptr;
public:
IntArray(unsigned length)
: m_length(length)
{
m_data = new int[length];
}
IntArray(const std::initializer_list<int>& list)
: IntArray(list.size())
{
int count = 0;
for (auto& element : list)
{
m_data[count] = element;
++count;
}
//for (unsigned count = 0; count < list.size(); ++count)
// m_data[count] = list[count]; //error
// initializer list는 []를 제공안함.
}
IntArray& operator = (const std::initializer_list<int>& list)
{
cout << "Assignment operator" << endl;
delete[] this->m_data;
int length = list.size();
int count = 0;
for (auto& element : list)
{
this->m_data[count] = element;
++count;
}
return *this;
}
friend ostream& operator << (ostream& out, IntArray& arr)
{
for (unsigned i = 0; i < arr.m_length; ++i)
out << arr.m_data[i] << " ";
out << endl;
return out;
}
~IntArray()
{
delete[] this->m_data;
}
};
int main()
{
int my_arr1[5] = { 1,2,3,4,5 };
int* my_arr2 = new int[5]{ 1,2,3,4,5 };
auto il = { 10,20,30 };
IntArray int_array{ 1,2,3,4,5 };
IntArray copy{ 3,4,5,6,7,8,9 };
int_array = copy;
cout << int_array << endl;
return 0;
}
주의해야할 점은, initializer_list는 []을 지원하지 않기에, 보통 for-each구문을 활용한다.
자세한 건 나중에 더 강의 듣고 기록 예정.
또 연산자 오버로딩으로 더 기능을 잘 구현할 수도 있음.
= 나 { } 나 같은 거임 ㅇㅇ
'C++(따라하며 배우는 C++)' 카테고리의 다른 글
Chapter 10-3. 집합 관계 (0) | 2021.10.28 |
---|---|
Chapter 10-1,2. 객체들 사이의 관계, 구성 관계 (0) | 2021.10.28 |
chapter 9-(2). 연산자 오버로 (0) | 2021.10.10 |
Chapter 9-(1). 연산자 오버로딩 (0) | 2021.09.28 |
Chapter 8-(2). 객체지향의 기초 (0) | 2021.09.14 |