검색결과 리스트
Study/Effective C++ 3판에 해당되는 글 12건
- 2009/02/17 항목 12. 객체의 모든 부분을 빠짐없이 복사하자.
- 2009/02/17 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자
- 2009/02/15 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자
- 2009/02/15 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자.
- 2008/07/10 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자.
- 2008/07/01 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자.
- 2008/06/27 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자.
- 2008/06/27 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자
- 2008/06/10 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자.
- 2008/06/10 항목 3. 낌새만 보이면 const를 들이대 보자!
글
내용
객체의 안쪽 부분을 캡슐화한 객체 지향 시스템 중 설계가 잘 된 것들을 보면, 객체를 복사 하는 함수가 딱 두개만 있는 것을 볼 수 있다. 이 둘을 복사 생성자와 복사 대입 연산자라 하고, 이 둘을 통틀어 객체 복사 함수(copying function)라 부른다.
컴파일러가 생성한 복사 함수는 기본적인 요구에 충실하다. 복사되는 객체가 갖고 있는 데이터를 빠짐없이 복사한다.
주의점
만약 컴파일러가 만든 기본 동작에 마음에 안들어 직접 복사 함수를 선언한다면 다음과 같은 것들을 지켜야 한다.
- 기존 클래스에 멤버를 추가하면 복사 함수를 수정 해줘야 한다.
- 파생 클래스에서 기본 클래스의 복사 함수를 호출 하도록 만들어야 한다.
CBase { ... } CTest { private: INT m_iMember; } CTest::CTest(const CTest& rhs) : CBase(rhs), // 기본 클래스의 복사 생성자를 호출한다. m_iMember(rhs.m_iMember) { } CTest& CTYest::operator=(const CTest&rhs) { CBase::operator=(rhs); m_iMember = rhs.m_iMember; // 기본 클래스 부분을 대입한다. return *this; }
이것만은 잊지 말자!
- 객체 복사 함수는 주어진 객체의 모든 데이터 멤버 및 모든 기본 클래스 부분을 빠드리지 말고 복사해야 한다.
- 클래스의 복사 함수 두개를 구현할 떄, 한쪽을 이용해서 다른 쪽을 구현하려는 시도는 절대로 하지 말자.그 대신, 공통된 동작을 제 3의 함수에다 분리해 놓고 양쪽에서 이것을 호출하게 만들어서 해결하자.
관련링크
http://ikpil.tistory.com/414
http://kldp.org/node/78631
http://rookiecj.tistory.com/8
http://ikpil.tistory.com/298
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 12. 객체의 모든 부분을 빠짐없이 복사하자. (0) | 2009/02/17 |
|---|---|
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
트랙백
댓글
글
내용
자기대입(self assignment) : 어떤 객체가 자기 자신에 대해 대입 연산자를 적용 하는 것을 말한다.
a[i] = a[j]; 또는 *px = *py; 는 자기대입의 가능성을 가지고 있는 문장이다. 어뜻 보기에 명확하지 않은 이러한 자기대입이 생기는 이유는 여러 곳에서 하나의 객체를 참조하는 상태, 즉 중복참조(aliasing)라고 불리는 것 때문이다.
그렇기 때문에, 같은 타입으로 만들어진 객체 여러개를 참조자 혹은 포인터로 물어 놓고 동작하는 코드를 작성할 떄는 같은 객체가 사용 될 가능성을 고려 하는것이 일반적으로 바람직한 자세이다.
해결법
- operator=의 첫머리에서 일치성 검사(identity test)를 통해 자기대입을 점검한다.
자기대입의 경우 많이 일어나는 것이 아니기 때문에 모든 객체에 대해서 비교하는 것은 효율이 낮다.
- 문장의 순서를 적절히 조정한다.
- 복사 후 맞바꾸기(copy and swap) 기법을 사용한다.
class CEx { ... };
CTest& CTest::operator=(const CTest& rhs)
{
if ( this == &rhs) return *this; // 객체가 같은지, 즉 자기대입인지 검사한다.
// 자기대입이면 아무것도 안한다.
delete pMember;
pMember = new CEx(*rhs.pMember);
return *this;
}
class CEx { ... };
CTest& CTest::operator=(const CTest& rhs)
{
CEx *pOrig = pMember; // 원래의 pMember를 어딘가에 기억해 둔다.
pMember = new CEx(*rhs.pMember);// 다음, pMember가 *pMember의 사본을 가르키게 만든다.
delete pOrig; // 원래의 pMember를 삭제한다.
return *this;
}
class CTest {
...
void swap(CTest& rhs); // *this의 데이터 및 rhs의 데이터를 맞바꾼다.
...
};
CTest& CTest::operator=(const CTest& rhs)
{
CTest temp(rhs); // rhs의 데이터에 대한 사본을 하나 만든다.
swap(temp); // *this의 데이터를 그 사본의 것과 맞바꾼다.
return *this;
}
이것만은 잊지 말자!
- operator=을 구현할 때, 어떤 객체가 그 자신에 대입되는 경우를 제대로 처리하도록 만들자. 원본 객체와 복사대상 객체의 주소를 비교해도 되고, 문장의 순서를 적절히 조정할 수도 있으며, 복사 후 맞바꾸기 기법을 써도 된다.
- 두 개 이상의 객체에 대해 동작하는 함수가 있다면, 이 함수에 넘겨지는 객체들이 사실 같은 객체인 경우 정확하게 동작하는지 확인해 보자.
관련링크
http://ikpil.tistory.com/413
http://jangyeol.springnote.com/pages/348598.xhtml
http://evax.springnote.com/pages/871438
http://ilu8318.egloos.com/1705010
http://ikpil.tistory.com/299
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 12. 객체의 모든 부분을 빠짐없이 복사하자. (0) | 2009/02/17 |
|---|---|
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
트랙백
댓글
글
이유
일종의 관례이므로 관례를 지키는 것이 좋다.
내용
C++의 대입 연산은 x = y = z = 15; 처럼 여러 개가 사슬 처럼 엮일 수 있다.
이처럼 대입 연산이 사슬처럼 엮이려면 대입 연산잔가 좌변 인자에 대한 참조자를 반환하도록 구현이 되어 있다.
이런 구현은 일종의 관례(convention)인데, 나름대로 만드는 클래스에 대입 연산자가 혹 들어간다면 이 관례를 지키는 것이 좋다.
이것만은 잊지 말자!
- 대입 연산자는 *this의 참조자를 반환하도록 만들자.
관련링크
http://ikpil.tistory.com/412
http://redinlife.egloos.com/1604282
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 12. 객체의 모든 부분을 빠짐없이 복사하자. (0) | 2009/02/17 |
|---|---|
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
트랙백
댓글
글
이유
1. 호출한 결과가 원하는 대로 돌아가지 않을 것이다.
2. 제대로 돌아간다 해도 폭탄을 가지고 있는 것과 같다.
설명
파생 클래스 객체가 생성될 때 그 객체의 기본 클래스 부분이 파생 클래스 부분보다 먼저 호출된다. 그렇기에 기본 클래스의 생성자가 호출될 동안에는, 가상 함수는 절대로 파생 클래스 쪽으로 내려가지 않는다.
기본 클래스 생성자는 파생 클래스 생성자보다 먼저 실행되기 때문에, 기본 클래스 생성자가 돌아가고 있을 때 파생 클래스 데이터 멤버는 아직 초기화 된 상태가 아닌 것이 핵심이다.
객체가 소멸 될 때는 파생 클래스의 소멸자가 일단 호출되고 파생 클래스만의 데이터 멤버는 정의되지 않은 값으로 가정하기 때문에, C++은 이들을 없는 것처럼 취급하고 진행한다. 기본 클래스 소멸자에 진입할 떄 객체는 기본 클래스 객체가 되며, 모든 C++ 기능들 (가상 함수, dynamic_cast, 기타 등등) 역시 기본 클래스 객체의 자격으로 처리한다.
참조
상속관계의 클래스 호출 순서는 다음과 같다.
1. 클래스 생성 시
이것만은 잊지 말자!
- 생성자 혹은 소멸자 안에서 가상 함수를 호출하지 말자. 가상 함수라고 해도, 지금 실행 중인 생성자나 소멸자에 해당되는 클래스의 파생 클래스 쪽으로는 내려가지 않는다.
관련링크
http://ikpil.tistory.com/410
http://evax.springnote.com/pages/871402
http://ljh131.tistory.com/16
2. 제대로 돌아간다 해도 폭탄을 가지고 있는 것과 같다.
설명
파생 클래스 객체가 생성될 때 그 객체의 기본 클래스 부분이 파생 클래스 부분보다 먼저 호출된다. 그렇기에 기본 클래스의 생성자가 호출될 동안에는, 가상 함수는 절대로 파생 클래스 쪽으로 내려가지 않는다.
기본 클래스 생성자는 파생 클래스 생성자보다 먼저 실행되기 때문에, 기본 클래스 생성자가 돌아가고 있을 때 파생 클래스 데이터 멤버는 아직 초기화 된 상태가 아닌 것이 핵심이다.
객체가 소멸 될 때는 파생 클래스의 소멸자가 일단 호출되고 파생 클래스만의 데이터 멤버는 정의되지 않은 값으로 가정하기 때문에, C++은 이들을 없는 것처럼 취급하고 진행한다. 기본 클래스 소멸자에 진입할 떄 객체는 기본 클래스 객체가 되며, 모든 C++ 기능들 (가상 함수, dynamic_cast, 기타 등등) 역시 기본 클래스 객체의 자격으로 처리한다.
참조
상속관계의 클래스 호출 순서는 다음과 같다.
1. 클래스 생성 시
- 기본 클래스 생성자 호출 후 기본 클래스 멤버 객체 초기화
- 파생 클래스 생성자 호출 후 파생 클래스 멤버 객체 초기화
- 파생 클래스 소멸자 호출 후 파생 클래스 멤버 객체 소멸
- 기본 클래스 소멸자 호출 후 기본 클래스 멤버 객체 소멸
이것만은 잊지 말자!
- 생성자 혹은 소멸자 안에서 가상 함수를 호출하지 말자. 가상 함수라고 해도, 지금 실행 중인 생성자나 소멸자에 해당되는 클래스의 파생 클래스 쪽으로는 내려가지 않는다.
관련링크
http://ikpil.tistory.com/410
http://evax.springnote.com/pages/871402
http://ljh131.tistory.com/16
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 12. 객체의 모든 부분을 빠짐없이 복사하자. (0) | 2009/02/17 |
|---|---|
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
트랙백
댓글
글
class DBConnection {
public:
static DBConnection create();
void close();};
위의 DBConnection 객체에 대해 사용자가 cloase를 직접 호출해야 하는 설계이다. 사용자의 망각을 사전에 차단하는 좋은 방법이라면 DBConnection에 대한 자원관리 클래스를 만들어서 그 클래스의 소멸자에서 close를 호출하게 만드는 것이다.
class DBConn {
// DBConnection 객체를 관리하는 클래스
public:
...
~DBConn() // 데이터베이스 연결이 항상 닫히도록 확실히 챙겨주는 함수
{
db.cloase();
}
private:
DBConnection db;
};
위의 두 클래스를 활용하여 다음과 같은 프로그래밍이 가능해진다.
{ // 블록 시작
DBConn dbc(DBConnection::create()); // DBConneciton 객체를 생성하고
// 이것을 DBConn 객체로
// 넘겨서 관리를 맡긴다.
... // DBConn 인터페이스를 통해 DBConnection 객체를 사용한다.
} // 블록 끝. DBConn 객체가 여기서 소멸된다.
// 따라서 dBConnection 객체에 대한 close 함수의 호출이
// 자동으로 이루어진다.
close 호출만 일사천리로 성공하면 아무 문제될 것이 없는 코드 이지만 만약 close 호출햇는데 여기서 예외가 발생했다고 가정하면 DBConn의 소멸자는 분명히 이 예외를 전파할 것이다.
class DBConn {
public:
...
void close()
{
db.close();
closed = true;
}
~DBConn()
{
if (!closed)
try {
db.close();
}
catch (...) {
close 호출이 실패 했다는 로그를 작성한다.
...
}
}
private:
DBConnection db;
bool closed;
};
어떤 동작이 예외를 일으키면서 실패할 가능성이 있고 또 그 예외를 처리해야 할 필요가 있다면, 그 예외는 소멸자가 아닌 다른 함수에서 비롯된 것이어야 한다는 것이 포인트 이다.
이것만은 잊지 말자!
- 소멸자에서는 예외가 빠져나가면 안된다. 만약 소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외든지 소멸자에서 모두 받아낸 후에 삼켜버리든지 프로그램을 끝내든지 해야 한다.,
- 어떤 클래스의 연산이 진행되다가 던진 예외에 대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 보통의 함수),즉 소멸자가 아닌 함수)이어야 한다.
관련 링크
http://ikpil.tistory.com/409
http://ikpil.tistory.com/365
http://redinlife.egloos.com/1627105
http://ilu8318.egloos.com/1705005
http://flashcafe.org/bbs/board.php?bo_table=programming_study&wr_id=83
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 12. 객체의 모든 부분을 빠짐없이 복사하자. (0) | 2009/02/17 |
|---|---|
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
트랙백
댓글
글
C++의 규정에 의하면, 기본 클래스 포인터를 통해 파생 클래스 객체가 삭제될 때 그 기본 클래스에 비가상 소멸자가 들어 있으면 플그램 동작은 미정의 사항이라 되어 있다. 대게 그 객체의 파생 클래스 부분이 소멸되지 않게 된다.
가상 소멸자를 선언하는 것은 그 클래스에 가상 함수가 하나라도 들어 있는 경우에만 한정하자.
(이부분은 뭐라 정리하기 힘들다 이해는 했는데 내용 정리가 힘들다 추후 다시 정리 예정)
이것만은 잊지 말자!
- 다형성을 가진 기본 클래스에는 반드시 가상 소멸자를 선언해야 한다. 즉, 어떤 크랠스가 가상 함수를 하나라도 갖고 있으면, 이 클래스의 소멸자도 가상 소멸자이어야 한다.
- 기본 클래스로 설계되지 않았거나 다형성을 갖도록 설계되지 않는 클래스에는 가상 소멸자를 선언하지 말아야 한다.
관련 링크
http://ikpil.tistory.com/408
http://ikpil.tistory.com/296
http://www.kwak101.pe.kr/bbs/view.php?id=kwk_worksBBS&no=159
가상 소멸자를 선언하는 것은 그 클래스에 가상 함수가 하나라도 들어 있는 경우에만 한정하자.
(이부분은 뭐라 정리하기 힘들다 이해는 했는데 내용 정리가 힘들다 추후 다시 정리 예정)
이것만은 잊지 말자!
- 다형성을 가진 기본 클래스에는 반드시 가상 소멸자를 선언해야 한다. 즉, 어떤 크랠스가 가상 함수를 하나라도 갖고 있으면, 이 클래스의 소멸자도 가상 소멸자이어야 한다.
- 기본 클래스로 설계되지 않았거나 다형성을 갖도록 설계되지 않는 클래스에는 가상 소멸자를 선언하지 말아야 한다.
관련 링크
http://ikpil.tistory.com/408
http://ikpil.tistory.com/296
http://www.kwak101.pe.kr/bbs/view.php?id=kwk_worksBBS&no=159
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 12. 객체의 모든 부분을 빠짐없이 복사하자. (0) | 2009/02/17 |
|---|---|
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
트랙백
댓글
글
하나밖에 없는 클래스 즉, 똑같은게 없는 클래스를 만든다고 할 때 객체는 사본(copy)를 만드는 것 자체가 이치에 맞지 않다. 그러다 보니 객체를 복사하려 하는 코드는 컴파일이 되지 않게 하려면 어떻게 해야 할까?
해결 방법
- 컴파일러가 생성하는 복사 생성자와 복사 대입 연산자는 public 으로 자동 생성 해버리므로 이들을 private 멤버로 선언 하면 된다.
ㄴ 효과1 : 클래스 멤버 함수가 명시적으로 선언되어 컴파일러는 자시느이 기본 버전을 만들 수 없다.
ㄴ 효과2 : 비공개(private) 접근성을 가지므로 외부에서 호출 할 수 없게 된다.
- private 멤버 함수는 그 클래스의 멤버 함수 및 프렌드 함수가 호출 할 수 있으므로 함수 자체를 정의를 하지 않으면 된다.
ㄴ 효과1 : 멤버 함수 및 프렌드 함수 까지의 접근을 완벽히 차단할 수 있다.
위의 꼼수는 [멤버 함수를 private 멤버로 서언하고 일부러 정의(구현)하지 않는 방법] 은 꽤 널리 퍼지면서 하나의 '기법' 화가 되어 C++의 iostream 라이브러리에 속한 몇몇 클래스에서도 복사 방지책으로 쓰이고 있다.
이것만은 잊지 말자!
- 컴파일러에서 자동으로 제공하는 기능을 허용치 않으려면, 대응되는 멤버 함수를 private로 선언한 후에 구현은 하지 않은 채로 두자. Uncopyable과 비슷한 기본 클래스를 쓰는것도 한 방법이다.
관련링크
http://ikpil.tistory.com/407
http://yesarang.tistory.com/42
http://ikpil.tistory.com/318
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1); // h1을 복사하려 한다. - 컴파일이 되면 안된다.
h1 = h2; // h2를 복사 하려 한다. - 컴파일이 되면 안된다.
HomeForSale h2;
HomeForSale h3(h1); // h1을 복사하려 한다. - 컴파일이 되면 안된다.
h1 = h2; // h2를 복사 하려 한다. - 컴파일이 되면 안된다.
해결 방법
- 컴파일러가 생성하는 복사 생성자와 복사 대입 연산자는 public 으로 자동 생성 해버리므로 이들을 private 멤버로 선언 하면 된다.
ㄴ 효과1 : 클래스 멤버 함수가 명시적으로 선언되어 컴파일러는 자시느이 기본 버전을 만들 수 없다.
ㄴ 효과2 : 비공개(private) 접근성을 가지므로 외부에서 호출 할 수 없게 된다.
- private 멤버 함수는 그 클래스의 멤버 함수 및 프렌드 함수가 호출 할 수 있으므로 함수 자체를 정의를 하지 않으면 된다.
ㄴ 효과1 : 멤버 함수 및 프렌드 함수 까지의 접근을 완벽히 차단할 수 있다.
위의 꼼수는 [멤버 함수를 private 멤버로 서언하고 일부러 정의(구현)하지 않는 방법] 은 꽤 널리 퍼지면서 하나의 '기법' 화가 되어 C++의 iostream 라이브러리에 속한 몇몇 클래스에서도 복사 방지책으로 쓰이고 있다.
class HomeForSale {
public:
...
private:
...
HomeForSale(const HomeForSale&); // 선언만 되어 있다.
HomeForSale& operator=(const HomeForSale);
}
public:
...
private:
...
HomeForSale(const HomeForSale&); // 선언만 되어 있다.
HomeForSale& operator=(const HomeForSale);
}
이것만은 잊지 말자!
- 컴파일러에서 자동으로 제공하는 기능을 허용치 않으려면, 대응되는 멤버 함수를 private로 선언한 후에 구현은 하지 않은 채로 두자. Uncopyable과 비슷한 기본 클래스를 쓰는것도 한 방법이다.
관련링크
http://ikpil.tistory.com/407
http://yesarang.tistory.com/42
http://ikpil.tistory.com/318
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
|---|---|
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
| 항목 1. C++를 언어들의 연합체로 바라보는 안목은 필수 (1) | 2008/05/28 |
트랙백
댓글
글
복사 생성자, 복사 대입 연산자, 생성자, 소멸자 는 사용자가 선언을 하지 않아도 컴파일러가 자동으로 public inline 함수로 선언해 버린다.
위의 두 클래스는 같다고 보면 된다.
참조
1. 복사 생성자를 제외한 생성자를 선언하면 컴파일러가 기본 생성자는 만들지 않는다.
이것만은 잊지 말자!
- 컴파일러는 경우에 따라 클래스에 대해 기본 생성자, 복사 생성자, 복사 대입 연산자, 소멸자를 암시적으로 만들어 놓을 수 있다.
관련링크
http://ikpil.tistory.com/406
http://zeniroy.springnote.com/pages/17563
http://chiarang.egloos.com/1685406
http://ikpil.tistory.com/346
http://redinlife.egloos.com/1611549
http://chiarang.egloos.com/1685433
http://chiarang.egloos.com/1685521
http://babonamu.egloos.com/1090073
calss Empty{};
class Empty {
public:
Empty() { ... } // 기본 생성자
Empty(const Empty& rhs) { ... } // 복사 생성자
~Empty() { ... } // 소멸자
Empty& operator= (const Empty& rhs) { ... } // 복사 대입 연산자
public:
Empty() { ... } // 기본 생성자
Empty(const Empty& rhs) { ... } // 복사 생성자
~Empty() { ... } // 소멸자
Empty& operator= (const Empty& rhs) { ... } // 복사 대입 연산자
위의 두 클래스는 같다고 보면 된다.
참조
1. 복사 생성자를 제외한 생성자를 선언하면 컴파일러가 기본 생성자는 만들지 않는다.
이것만은 잊지 말자!
- 컴파일러는 경우에 따라 클래스에 대해 기본 생성자, 복사 생성자, 복사 대입 연산자, 소멸자를 암시적으로 만들어 놓을 수 있다.
관련링크
http://ikpil.tistory.com/406
http://zeniroy.springnote.com/pages/17563
http://chiarang.egloos.com/1685406
http://ikpil.tistory.com/346
http://redinlife.egloos.com/1611549
http://chiarang.egloos.com/1685433
http://chiarang.egloos.com/1685521
http://babonamu.egloos.com/1090073
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
|---|---|
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
| 항목 1. C++를 언어들의 연합체로 바라보는 안목은 필수 (1) | 2008/05/28 |
트랙백
댓글
글
초기화되지 않은 값을 읽도록 내버려 두면 정의되지 않은 동작이 그대로 흘러 나오게 된다.
모든 객체를 사용하기 전에 항상 초기화 하자! 기본제공 타입으로 만들어진 비멤버 객체에 대해서는 초기화를 손수 해야 한다.
이런 부분을 제외하고 나면, C++의 초기화의 나머지 부분은 생성자로 귀결된다. 생성자에서 지킬 규칙은 간단하다. 그 객체의 모든 것을 초기화하자! 단 대입(assignment)을 초기화(initialization)와 헷갈리지 말자!
C++ 객체를 구성하는 데이터의 초기화 순서
1. 기본 클래스는 파생 클래스보다 먼저 초기화된다.
2. 클래스 데이터 멤버는 그들이 선언된 순서대로 초기화 된다.
정적 객체(static object) 의 종류
1. 전역 객체.
2. 네임스페이스 유효범위에서 정의된 객체.
3. 클래스 안에서 static으로 선언된 객체.
4. 함수 안에서 static으로 선언된 객체.
5. 파일 유효범위에서 static으로 정의된 객체
정적 객체(static object) 의 주의점
1. 이중에서 함수 안에 있는 객체는 지역 정적 객체(local static object)라고 하고, 나머지는 비지역 정적 객체(non-local static object)라고 한다. 정적 객체는 프로그램이 끝날 떄 자동으로 소멸된다.
2. 별개의 지역에서 정의된 비지역 정적 객체들의 초기화 순서는 정해져 있지 않으므로 주의 하자.
이것만은 잊지 말자!
- 기본제공 타입의 객체는 직접 손으로 초기화 한다. 경우에 따라 저절로 되기도 하고 안되기도 하기 떄문이다.
- 생성자에서는 데이터 멤버에 대한 대입문을 생성자 본문 내부에 넣는 방법으로 멤버를 초기화 하지 말고 멤버 초기화 리스트를 즐겨 사용하자. 그리고 초기화 리스트에 데이터 멤버르 ㄹ나열할 떄는 클래스에 각 데이터 멤버가 선언된 순서와 똑같이 나열하자.
- 여러 번역 단위에 있는 비지역 정적 객체들의 초기화 순서 문제는 피해서 설계해야 한다. 비지역 정적 객체를 지역 정적 객체로 바꾸면 된다.
관련링크
http://ikpil.tistory.com/405
모든 객체를 사용하기 전에 항상 초기화 하자! 기본제공 타입으로 만들어진 비멤버 객체에 대해서는 초기화를 손수 해야 한다.
int x = 0; // int의 직접 초기화
const char* text = "A C-style string"; // 포인터의 직접 초기화
double d; // 입력 스트림에서 읽음으로써
std::cin >> d; // "초기화" 수행
const char* text = "A C-style string"; // 포인터의 직접 초기화
double d; // 입력 스트림에서 읽음으로써
std::cin >> d; // "초기화" 수행
이런 부분을 제외하고 나면, C++의 초기화의 나머지 부분은 생성자로 귀결된다. 생성자에서 지킬 규칙은 간단하다. 그 객체의 모든 것을 초기화하자! 단 대입(assignment)을 초기화(initialization)와 헷갈리지 말자!
class PhoneNumber { ... };
class ABEntry {
public:
ABEntry(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones);
private:
std::string theName;
std::string theAddress;
std::list<phoneNumber> thePhones;
int numTimesConsulted;
};
ABEntry::ABEntry(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones)
{
theName = name; // 이것은 초기화가 아닌 '대입' 이다.
theAddress = adress;
thePhones = phones;
numTimesConsulted = 0;
}
class ABEntry {
public:
ABEntry(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones);
private:
std::string theName;
std::string theAddress;
std::list<phoneNumber> thePhones;
int numTimesConsulted;
};
ABEntry::ABEntry(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones)
{
theName = name; // 이것은 초기화가 아닌 '대입' 이다.
theAddress = adress;
thePhones = phones;
numTimesConsulted = 0;
}
ABEntry::ABEntry(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones)
: theName(name), // 이것이 바로 '초기화' 이다.
theAddress(adress),
thePhones(phones),
numTimesConsulted(0)
{} // 생성자에는 아무것도 없다.
const std::list<PhoneNumber>& phones)
: theName(name), // 이것이 바로 '초기화' 이다.
theAddress(adress),
thePhones(phones),
numTimesConsulted(0)
{} // 생성자에는 아무것도 없다.
C++ 객체를 구성하는 데이터의 초기화 순서
1. 기본 클래스는 파생 클래스보다 먼저 초기화된다.
2. 클래스 데이터 멤버는 그들이 선언된 순서대로 초기화 된다.
정적 객체(static object) 의 종류
1. 전역 객체.
2. 네임스페이스 유효범위에서 정의된 객체.
3. 클래스 안에서 static으로 선언된 객체.
4. 함수 안에서 static으로 선언된 객체.
5. 파일 유효범위에서 static으로 정의된 객체
정적 객체(static object) 의 주의점
1. 이중에서 함수 안에 있는 객체는 지역 정적 객체(local static object)라고 하고, 나머지는 비지역 정적 객체(non-local static object)라고 한다. 정적 객체는 프로그램이 끝날 떄 자동으로 소멸된다.
2. 별개의 지역에서 정의된 비지역 정적 객체들의 초기화 순서는 정해져 있지 않으므로 주의 하자.
이것만은 잊지 말자!
- 기본제공 타입의 객체는 직접 손으로 초기화 한다. 경우에 따라 저절로 되기도 하고 안되기도 하기 떄문이다.
- 생성자에서는 데이터 멤버에 대한 대입문을 생성자 본문 내부에 넣는 방법으로 멤버를 초기화 하지 말고 멤버 초기화 리스트를 즐겨 사용하자. 그리고 초기화 리스트에 데이터 멤버르 ㄹ나열할 떄는 클래스에 각 데이터 멤버가 선언된 순서와 똑같이 나열하자.
- 여러 번역 단위에 있는 비지역 정적 객체들의 초기화 순서 문제는 피해서 설계해야 한다. 비지역 정적 객체를 지역 정적 객체로 바꾸면 된다.
관련링크
http://ikpil.tistory.com/405
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
|---|---|
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
| 항목 1. C++를 언어들의 연합체로 바라보는 안목은 필수 (1) | 2008/05/28 |
트랙백
댓글
글
const 사용처
클래스 바깥에서는 전역 혹은 네임스페이스 유효범위의 상수를 선언(정의)하는 데 쓸 수 있다.
파일, 함수, 블록 유효범위에서 static으로 선언한 객체에도 const를 붙일 수 있다.
클래스 내부의 경우 정적 멤버 및 비정적 데이터 멤버 모두를 상수로 선언할 수 있다.
char greeting[] = "Hello";
char *p = greeting; // 비상수 포인터, 비상수 데이터
const char *p = greeting; // 비상수 포인터, 상수 데이터
char * const p = greeting; // 상수 포인터, 비상수 데이터
const char * const p = greeting; // 상수 포인터, 상수 데이터
char *p = greeting; // 비상수 포인터, 비상수 데이터
const char *p = greeting; // 비상수 포인터, 상수 데이터
char * const p = greeting; // 상수 포인터, 비상수 데이터
const char * const p = greeting; // 상수 포인터, 상수 데이터
void f1(const Widget *pw); // f1은 상수 Widget 객체에 대한 포인터를 매개 변수로 취한다.
void f2(Widget const *pw); // f2도 f1과 동일하다.
void f2(Widget const *pw); // f2도 f1과 동일하다.
std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin();
// iter는 T* const처럼 동작한다.
*iter = 10; // OK, iter가 가르키는 대상을 변경한다.
++iter; // Error! iter는 상수이다.
std::vector<int>::const_iterator cIter = vec.begin();
// cIter는 const T*처럼 동작한다.
*cIter = 10; // Error! *cIter가 상수이다.
++cIter; // OK, 문제 없다.
...
const std::vector<int>::iterator iter = vec.begin();
// iter는 T* const처럼 동작한다.
*iter = 10; // OK, iter가 가르키는 대상을 변경한다.
++iter; // Error! iter는 상수이다.
std::vector<int>::const_iterator cIter = vec.begin();
// cIter는 const T*처럼 동작한다.
*cIter = 10; // Error! *cIter가 상수이다.
++cIter; // OK, 문제 없다.
이것만은 잊지 말자!
- const를 분텨 선언하면 컴파일러가 사용상의 에러를 잡아내는데 도움을 준다. const는 어떤 유효범위에 있는 개체에도 붙을 수 있으며, 함수 매개변수 밑 반환 타입에도 붙을 수 있으며, 멤버 함수에도 붙을 수 있다.
- 컴파일러 쪽에서 보면 비트수준 상수성을 지켜야 하지만, 개념적인(논리적인) 상수성을 사용해서 프로그래밍해야 한다.
- 상수 멤버 및 비상수 멤버 함수가 기능적으로 서로 똑같게 구현되어 있을 경우에는 코드 중복을 피하는 것이 좋은데, 이때 비상수 버전이 상수 버전을 호출하도록 만든다.
관련링크
http://ikpil.tistory.com/402
'Study > Effective C++ 3판' 카테고리의 다른 글
| 항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2009/02/17 |
|---|---|
| 항목 10. 대입 연산자는 *this 참조자를 반환하게 하자 (0) | 2009/02/15 |
| 항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자. (0) | 2009/02/15 |
| 항목 8. 예외가 소멸자를 떠나지 못하도록 붙들여 놓자. (0) | 2008/07/10 |
| 항목 7. 다형성을가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2008/07/01 |
| 항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. (0) | 2008/06/27 |
| 항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2008/06/27 |
| 항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자. (0) | 2008/06/10 |
| 항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2008/06/10 |
| 항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자. (0) | 2008/05/29 |
| 항목 1. C++를 언어들의 연합체로 바라보는 안목은 필수 (1) | 2008/05/28 |
RECENT COMMENT