INDEX
복사생성자
- 얕은 복사
- 값만 복사하는 형태
- 포인터 등을 복사 시 같은 주소를 참조하기 때문에, 값 변경, 동적 해제 등에서 문제가 발생한다.
- 깊은 복사
- 주소가 가리키는 값, 즉 그 메모리의 값을 복사하는 형태
- 포인터 등을 복사 시 한 주소를 참조하지 않고 새로 주소를 할당받은 후 참조하기 때문에 문제가 발생하지 않는다.
- 복사생성자
- 같은 클래스에 속한 다른 개체를 이용하여 새로운 개채를 초기화
- 같은 크기, 같은 데이터
<class-name> (const <class-name>&);
- 같은 클래스에 속한 다른 개체를 이용하여 새로운 개채를 초기화
- 암시적 복사 생성자
- 코드에 복사 생성자가 없는 경우, 컴파일러가 암시적 복사 생성자를 자동 생성
- 암시적 복사 생성자는 얕은복사를 수행
- 멤버 별 복사
- 각 멤버의 값을 복사
- 개체인 멤버변수는 그 개체의 복사 생성자가 호출됨
- 깊은 복사 생성자?
- 클래스 안에서 동적으로 메모리를 할당하고 있다면? 얕은 복사가 위험할 가능성이 매우 높음!!
- So, 사용자가 만든 복사 생성자 사용
- 직접 복사 생성자를 만들어서 깊은 복사를 할것
- 포인터 변수가 가리키는 실제 데이터까지 복사
-
포인터를 가지고 있는 클래스
얕은 복사 생성자
// 앝은 복사 생성자 ClassRecord::ClassRecord(const ClassRecord& other) :mCount(other.mCount) , mScores(other.mScores) { }
깊은 복사 생성자
// 깊은 복사 생성자 ClassRecord::ClassRecord(const ClassRecord& other) : mCount(other.mCount) { mScores = new int[mCount]; memcpy(mScores, other.mScores, mCount*sizeof(int)); }
함수 오버로딩
- 함수 오버로딩
-
매개변수 목록을 제외하고는 모든게 동일
-
반환형은 상관 없음
void Print(int score); //OK void Print(const char* name); //OK void Print(float gpa, const char* name); //OK int Print(int score); //컴파일에러 int Print(float gpa); //OK
-
- 함수 오버로딩 매칭하기
- 오버로딩 함수 중에 어떤 함수를 호출해야 하는지 판단하는 과정
- 함수 매칭 결과는 3개가 있음
- 매칭되는 함수를 찾을 수 없음 -> 컴파일에러
- 매칭되는 함수를 여러 개 찾음 -> 컴파일 에러
- 가장 적합한 함수를 하나 찾음 -> OK
-
모호한 호출?
int Max(int, int); // 정확한 매치 : 1, 표준 변환 : 1 int Max(double, double); // 정확한 매치 : 1, 표준 변환 : 1 Max(1,3.14);
- 컴파일에러
연산자 오버로딩
연산자
-
함수처럼 작동하는 부호
-
C++에서는 프로그래머가 연산자를 오버로딩 할 수 있음
-
- C와 Java는 연산자 오버로딩을 지원하지 않음
-
연산자 오버리동 하는 방법은 두 가지
-
-
멤버 함수
-
멤버 아닌 함수
-
특정 연산자들은 멤버 함수를 이용해서만 오버로딩 가능
-
- =, (), [], ->
-
멤버 연산자 작성법
<return-type> <class-name>::operator<operator-symbol>(<argument-list>)
{
}
Vector Vector::operator-(const Vector& rhs) const;
Vector Vector::operator*(const Vector& rhs) const;
Vector Vector::operator/(const Vector& rhs) const;
멤버 아닌 연산자 오버로딩 작성법
//header
friend <return-type> operator<operator-symbol>(<argument-list);
//cpp
<return-type> operator<operator-symbol>(<argument-list>)
{
}
friend void operator<<(std::ostream& os, const Vector& rhs);
friend Vector operator*(int scalar, const Vector* lhs);
멤버 함수를 이용한 연산자 오버로딩
Vector.h
class Vector { public: Vector operator+(const Vector& rhs) const; private: int mX; int mY; };
Vector.cpp
Vector Vector::operator+(const Vecotr& rhs) const { Vector sum; sum.mX = mX + rhs.mX; sum.mY = mY + rhs.mY; return sum; }
Vector v1(10,20);
Vector v2(3,17);
Vector Sum = v1 + v2;
멤버 아닌 함수를 이용한 연산자 오버로딩
//Vector.h
class Vector
{
friend std::ostream& operator<<(const std::ostream& os, const Vector& rhs);
public:
...
private:
...
};
//Vector.cpp
std::ostream& operator<<(const std::ostream& os, const Vector& rhs)
{
os<<rhs.mX<<", "<<rhs.mY;
return os;
}
//main.cpp
Vector vector(10, 20);
std::cout << vector1 << std::endl;
- private 멤버 접근하고 싶으면? -> friend함수를 사용하면 된다.
friend키워드
-
클래스 정의 안에 friend 키워드를 사용 가능
- 다른 크래스나 함수가 나의 private 또는 protected멤버에 점근할 수 있게 해줌
-
friend함수는 멤버 함수가 아님
- 하지만 다른 클래스의 private멤버에 접근할 수 있음
-
friend클래스 예제
X.h
class X { friend class Y; 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; //컴파일에러 }
-
friend 함수 예제
X.h
class X { friend void Foo(X& x); private: int mPrivateInt; };
GlobalFucntion.cpp
void Foo(X& x) { x.mPrivateInt += 10; }
연산자 오버로딩과 const
const를 쓰는 이유
-
멤버변수의 값이 바뀌는 것을 방지
-
최대한 많은 곳에 const를 붙일 것!
- 지역변수에도!!
- 단 모든 회사가 이 코딩표준을 따르지는 않음
연산자 오버로딩과 const
Vector operator+(const Vector& rhs) const;
- 지역변수에도!!
-
const &
를 사용하는 이유?- 불필요한 개체의 사본이 생기는 것을 방지
- 멤버변수가 바뀌는 것도 방지
연산자 오버로딩에 const를 사용하지 않는 경우 예시
vector1 += vector2; vector1 = vector1.operator+=(vector2);
-
개체 복사가 없음
-
연상자 여럿을 연결해서 쓸 수 있음
제한사항
- 오버로딩된 연산자는 최소한 하나의 사용자정의형을 가져야 함
- 오버로딩된 연산자는 피연산자 수를 동일하게 유지해야함
- 새로운 연산자 부호를 만들 수 없음
- 오버로딩할 수 없는 연산자가 존재
- . .* :: ?: 등등
연산자 오버로딩을 남용하지 말것!
이해할 수 없는 문제가 생길 수도 있다!
-
Vector1 = Vector2 * Vector3
위의 코드는 외적인가 내적인가?
-
So! 차라리 함수를 만들 것
대입연산자
- operator=
- 복사생성자와 거의 동일
- 그러나 대입 연산자는 메모리를 해제해 줄 필요가 있을 수도 있다…
- 복사 생성자를 구현했다면 대입 연산자도 구현해야 할 것임
** 암시적 oprator=**
- 얕은 복사
암시적 함수들을 제거하는 법
클래스에 딸려오는 기본 함수들
- 매개변수 없는 생성자
- 복사 생성자
- 소멸자
- 대입(=) 연산자
클래스에 딸려오는 기본 함수들을 원하지 않는다면?
기본 생성자를 “지우는” 법
방법 1
//Vector.h class Vector { public: Vector(const Vector& other); private: int mX; int mY; }; //main.cpp Vector v1; // 컴파일 에러
방법2
//Vector.h class Vector { private: Vector(){}; int mX; int mY; }; //main.cpp Vector v1; // 컴파일 에러
암시적 복사 생성자를 “지우는”법
class Vector { public: Vector(){}; private: Vector(const Vector& other){}; int mX; int mY; };
암시적 소멸자를 “지우는”법
쓸 일이 있을까..?class Vector { public: private: ~Vector(){}; int mX; int mY; };
암시적 opertor=를 “지우는”법
class Vector { public: private: const Vector& operator=(const Vector& rhs); int mX; int mY; };