개체지향 프로그래밍 (OOP) 2
카테고리: Cpp
🤯 언리얼을 하기 위해 C++ 기억 되살리기 프로젝트
개체지향 프로그래밍 (OOP) 2
1. 복사 생성자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Vector.h
class Vector
{
public:
Vector(const Vector& other); // <- 받는 매개변수가 이러면 복사 생성자다
// 무슨 소리냐면
// const하고, 나 자신 클래스. 똑같은 클래스에 있는 개체를 하나 받는거.
// 저건 생성자이므로 나 자신을 대입할 순 없음. 생성되는 도중이므로. 존재도 안하니까.
// 같은 클래스에 있는 다른 개체를 매개변수로 받아서 생성하는 것.
private:
int mX;
int mY;
};
// Vector.cpp
Vector::Vector(const Vector& other)
: mX(other.mX), mY(other.mY)
{
}
- 같은 클래스에 속한 다른 개체를 이용하여 새로운 개체를 초기화한다는 것
- 같은 크기, 같은 데이터
<class-name> (const <class-name>&);
1
2
3
4
Vector(const Vector& other);
Vector a; // 매개변수 없는 생성자를 호출
Vector b(a); // 복사 생성자 호출
- 코드에 복사 생성자가 없는 경우, 컴파일러가 암시적 복사 생성자를 자동 생성
- 암시적 복사 생성자는 얕은 복사를 수행
- 멤버별 복사
- 각 멤버의 값을 복사함.
- 개체인 멤버변수는 그 개체의 복사 생성자가 호출됨
- 클래스 안에서 동적으로 메모리를 할당하고 있다면? 얕은 복사가 위험할 가능성이 높음
- 직접 복사 생성자를 만들어서 깊은 복사를 할 것
- 포인터 변수가 가리키는 실제 데이터까지도 복사
1 2 3 4 5 6 7 8
// 깊은 복사 ClassRecord::ClassRecord(const ClassRecord& other) : mCount(other.mCount) { mScores = new int[mCount]; memcpy(mScores, other.mScores, mCount * sizeof(int)); }
2. 연산자 오버로딩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Vector.h
class Vector
{
public:
Vector operator+(const Vector& rhs) const;
// 첫번째 매개변수는 나 자신, 그다음 연산자, 그리고 다른 벡터가 두번째
private:
int mX;
int mY;
};
// Vector.cpp
Vector Vector::operator+(const Vector& rhs) const
{
Vector sum;
sum.mX = mX + rhs.mX;
sum.mY = mY + rhs.mY;
return sum;
}
// main.cpp
Vector v1(10, 10);
Vector v2(3, 17);
Vector sum = v1 + v2; // <- 여기서 연산자 오버로딩
Vector sum = v1.operator+(v2); // 위와 동일!
std::cout << number;
std::cout.operator<<(number); // 위와 동일
2-1. friend 키워드
1
2
3
4
void operator<<(std::ostream& os, const Vector& rhs)
{
os << rhs.mX << ", " << rhs.mY;
}
위 코드에는 문제점이 있음.
- 이 전역함수를 어디에 넣지?
- 이 함수가 Vector 클래스의 private 멤버변수를 어떻게 읽지?
💡 friend 함수를 쓰면 된다!
- 클래스 정의 안에 friend 키워드를 사용 가능
- 다른 클래스나 함수가 나의 private 또는 protected 멤버에 접근 할 수 있게 허용 ```cpp // X.h class X { private: int mPrivateInt; }
// Y.h #include “x.h” class Y { public: void Foo(X& x); }
// Y.cpp void Y::Foo(X& x) { x.mPrivateInt += 10; // 컴파일 에러 }
1 2 3 4 5 6 7 8 9 10 11 12
고치려면... ```cpp // X.h class X { friend class Y; // <------- 이렇게 친구라고 선언! private: int mPrivateInt; }
이래서 friend는 oop 안티 패턴이라고도 함… 외부에서 쓰게 해주니까.
friend 함수도 있음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// X.h
class X
{
friend void Foo(X& x); // <----- 이렇게 friend 써서 접근 가능하게 함
private:
int mPrivateInt;
}
// GlobalFunction.cpp
void Foo(X& x)
{
x.mPrivateInt += 10;
}
- friend 함수는 멤버 함수가 아님!
- 하지만 다른 클래스의 private 멤버에 접근할 수 있음. 권한을 주는거임.
이게 정말 올바른가요?
1
2
3
void operator<<(std::ostream& os, const Vector& rhs);
std::cout << vector1 << std::endl; // 컴파일 에러
왜 컴파일 에러가 나냐면,
cout « vector1 해서 반환되는 값을 받아서 또 endl이 호출이 되는 구조인데,
« 오퍼레이터는 반환형이 void임… 받을 게 없으니 에러가 남.
1
2
3
4
5
6
7
8
9
10
11
// 이렇게 고쳐주면 됨!
std::ostream& operator<<(std::ostream& os, const Vector& rhs);
std::ostream& operator<<(std::ostream& os, const Vector& rhs)
{
os << rhs.mX << ", " << rhs.mY;
return os; // <- 요렇게 반환!
}
std::cout << vector1 << std::endl;
2-2. 연산자 오버로딩과 const
- const 를 쓰는 이유?
- 멤버 변수의 값이 바뀌는 것을 방지
- 최대한 많은 곳에 const를 붙일 것!
- const &를 사용하는 이유?
- 불필요한 개체의 사본이 생기는 것을 방지
- 멤버 변수가 바뀌는 것도 방지
1
Vector operator+(const Vector& rhs) const;
댓글남기기