'[ Programming ]' 카테고리의 다른 글
멤버 함수 보다는 비멤버 비프렌드 함수에 더 가깝게 쓰자. (0) | 2009.10.18 |
---|---|
사용자 정의 타입변환 함수에 대한 주의를 놓지 말자. (0) | 2009.10.07 |
컴파일러의 분석. (4) | 2009.07.31 |
상속 받는 기본 매개변수 값 변경 금지! (0) | 2009.07.29 |
인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하기. (0) | 2009.07.15 |
멤버 함수 보다는 비멤버 비프렌드 함수에 더 가깝게 쓰자. (0) | 2009.10.18 |
---|---|
사용자 정의 타입변환 함수에 대한 주의를 놓지 말자. (0) | 2009.10.07 |
컴파일러의 분석. (4) | 2009.07.31 |
상속 받는 기본 매개변수 값 변경 금지! (0) | 2009.07.29 |
인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하기. (0) | 2009.07.15 |
ㄱ. new로 생성한 것은 객체에 대해서는 유효 범위를 벗어 나면 스스로 지워 지는 것이 아니라, 직접 delete를 해 주어야 한다.
- for문을 사용 하여 delete 해 줄 시
① for_each 만큼 명확하지 않다.
② 예외 안전성이 갖추고 있지 않다.
- 포인터 컨테이너 대신에 스마트 포인터의 컨테이너를 사용하는 것.
- 스마트 포인터 사용시 auto_ptr은 피해야 함.
- boost라이브러리에 있는 shared_ptr을 사용.
class Widget { }; typedef std::vector< Widget* > VecWidget; /// for문을 사용한 해제. void doSomething() { VecWidget widget; for( VecWidget::iterator it = widget.begin(); it != widget.end(); ++it) { delete (*it); } } /// for_each문을 사용한 해제 - 1. template<typename t=""> struct DeleteObject : public unary_function<const t*,="" void=""> { void operator() (const T* ptr) const { delete ptr; } }; void doSomething() { VecWidget widget; /// Widget의 포인터를 삭제 하는 것이 당연하지만, 실수로 Widget 외의 /// 것을 할 경우 버그를 발생할 우려가 있음. for_each(widget.begin(), widget.end(), DeleteObject<widget>()) } /// for_each문을 사용한 해제 - 2. struct DeleteObject { template<typename t=""> void operator() (const T* ptr) const { delete ptr; } }; void doSomething() { VecWidget widget; /// Widget의 포인터를 삭제 하는 것이 당연하지만, 실수로 Widget 외의 /// 것을 할 경우 버그를 발생할 우려가 있음. for_each(widget.begin(), widget.end(), DeleteObject()) } </typename></widget></const></typename>
STL 항목 5 : 단일 요소를 단위로 동작하는 멤버 함수보다 요소의 범위를 단위로 동작하는 멤버 함수가 더 낫다. (0) | 2009.10.27 |
---|---|
STL 항목 4 : size()의 결과를 0과 비교할 생각이라면 차라리 empty를 호출하자. (0) | 2009.10.27 |
STL 항목 3 : 복사는 컨테이너 안의 객체에 맞게 비용은 최소화하며, 동작은 정확하게 하자. (1) | 2009.10.23 |
STL 항목 2 : “컨테이너에 독립적인 코드” 라는 환상을 조심하자. (0) | 2009.10.23 |
STL 항목 1 : 적재적소에 알맞은 컨테이너를 사용하자. (0) | 2009.10.22 |
ㄱ. 단일 요소 멤버 함수보다 범위 멤버 함수가 더 좋은 이유.
① 범위 멤버 함수를 사용한 코드가 대개 짧다.
② 범위 멤버 함수는 훨씬 명확하고 간결한 의미를 전달 한다.
ㄴ. 단일 요소 멤버 함수에서는 메모리 할당자의 요구 많음.
객체 복사도 빈번, 불필요한 연산도 자주 일어남.
같은 일은 한다면, 범위 멤버 함수는 이 보다 효율적.
ㄷ. copy를 사용하여 루프 없이 비슷해 보이지만 범위 단위로 동작하는 멤버 함수보다 비효율적인 면이 있다.
① 불필요한 함수 호출 – 범위 멤버 함수는 한번만 하면 되기 때문.
② 새로운 요소를 넣기 위해 들어 있던 요소를 밀어 넣는 비용.
③ 메모리 할당.
ㄹ. 범위 멤버 함수.
① 범위 생성 – 모든 컨테이너는 다음과 같은 생성자를 지원.
container::container(Inputlterator begin, Inputlterator end);
② 범위 삽입 – 모든 표준 컨테이너는 다음과 같은 insert를 지원
void contatiner::insert(iterator position, Inputiterator begin, Inputiterator end);
연관 컨테이너
void contatiner::insert(Inputiterator begin, Inputiterator end);
③ 범위 삭제
iterator contation::erase(iterator begin, iterator end); - 표준 시퀀스 컨테이너
void contation::erase(iterator begin, iterator end); - 표준 연관 컨테이너
④ 범위 대입 – 모든 표준 시퀸스 컨테이너
void contatiner::assign(Inputiterator begin, Inputiterator end);
#include "stdafx.h" #include < iostream > #include < string > #include < vector > class CTest { public: CTest(const int _kiID) : m_iID(_kiID) { cout << "ID : "<< m_iID << " 디폴트 생성자 호출" << endl; } CTest(const CTest& _krTest) : m_iID(_krTest.m_iID) { cout << "ID : "<< m_iID << "복사 생성자 호출" << endl; } const CTest& operator = (const CTest& _krTest) { this->m_iID = _krTest.m_iID; cout << "ID : "<< m_iID << "대입 연산자" << endl; return *this; } ~CTest() { cout << "ID : "<< m_iID << "소멸자" << endl; } private: int m_iID; }; int _tmain(int argc, _TCHAR* argv[]) { CTest TestArr[5] = {1, 2, 3, 4, 5}; cout << "=====================================================" << endl; std::vector< CTest > VecTest1; std::vector< CTest > VecTest2; VecTest1.insert(VecTest1.begin(), TestArr, TestArr + 5); cout << "=====================================================" << endl; copy(TestArr, TestArr + 5, inserter(VecTest2, VecTest2.begin())); return 0; }
STL 항목 7 : new로 생성한 포인터의 컨테이너를 사용할 때에는 컨테이너가 소멸되기 전에 포인터를 delete하는 일을 잊지 말자. (0) | 2009.10.28 |
---|---|
STL 항목 4 : size()의 결과를 0과 비교할 생각이라면 차라리 empty를 호출하자. (0) | 2009.10.27 |
STL 항목 3 : 복사는 컨테이너 안의 객체에 맞게 비용은 최소화하며, 동작은 정확하게 하자. (1) | 2009.10.23 |
STL 항목 2 : “컨테이너에 독립적인 코드” 라는 환상을 조심하자. (0) | 2009.10.23 |
STL 항목 1 : 적재적소에 알맞은 컨테이너를 사용하자. (0) | 2009.10.22 |
empty는 모든 표준 컨테이너에 대해 상수 시간에 수행 하지만, 몇몇 제품에서 구현된 list 클래스에서 size가 선형 시간에 수행 되기 때문에 컨테이너가 비어 있는지를 확인 할 때 empty를 사용하여 비용?을 줄이자
STL 항목 7 : new로 생성한 포인터의 컨테이너를 사용할 때에는 컨테이너가 소멸되기 전에 포인터를 delete하는 일을 잊지 말자. (0) | 2009.10.28 |
---|---|
STL 항목 5 : 단일 요소를 단위로 동작하는 멤버 함수보다 요소의 범위를 단위로 동작하는 멤버 함수가 더 낫다. (0) | 2009.10.27 |
STL 항목 3 : 복사는 컨테이너 안의 객체에 맞게 비용은 최소화하며, 동작은 정확하게 하자. (1) | 2009.10.23 |
STL 항목 2 : “컨테이너에 독립적인 코드” 라는 환상을 조심하자. (0) | 2009.10.23 |
STL 항목 1 : 적재적소에 알맞은 컨테이너를 사용하자. (0) | 2009.10.22 |
ㄱ. STL의 방식은 복사되어 ( 컨데이너에 ) 들어가고, 복사 되어 나온다.
이런 복사는 해당 클래스의 복사용 멤버 함수를 사용 ( 복사 생성자, 복사 대입 연산자 ).
ㄴ. STL의 기본 복사 방식으로 생기는 문제점.
① STL의 기본이 복사 방식으로 이루어 지기 때문에 데이터가 크면 클수록 메모리 사용량과 CPU 사용률은 더욱 높아 지는 문제점.
② 상속된 객체의 경우 복사 동작이 제대로 이루어 지지 않는 형상 – 슬라이스.
기본 클래스의 컨테이너를 만들고 파생 클래스를 객체를 넣으면, 복사 과정에서 기본 생성자가 쓰이면서 파생 클래스 부분에 선언된 데이터는 잘려나감.
ㄷ. 해결 방안.
객체의 컨테이너를 만들지 않고, 포인터의 컨테이너를 만듬.
① 속도가 빠름 – 포인터만 복사 하면 되기 때문
② 정확 – 포인터를 이루는 비트를 복사 하기 때문.
③ 슬라이스 문제 해결 – 포인터를 복사 될 때 잘려나가는 데이터가 생기지 않음.
이렇게 포인터의 컨테이너를 만들면 인해 메모리 관리에 대한 문제점이 생기지만 스마트 포인터를 사용하면 해결 가능.
STL 항목 7 : new로 생성한 포인터의 컨테이너를 사용할 때에는 컨테이너가 소멸되기 전에 포인터를 delete하는 일을 잊지 말자. (0) | 2009.10.28 |
---|---|
STL 항목 5 : 단일 요소를 단위로 동작하는 멤버 함수보다 요소의 범위를 단위로 동작하는 멤버 함수가 더 낫다. (0) | 2009.10.27 |
STL 항목 4 : size()의 결과를 0과 비교할 생각이라면 차라리 empty를 호출하자. (0) | 2009.10.27 |
STL 항목 2 : “컨테이너에 독립적인 코드” 라는 환상을 조심하자. (0) | 2009.10.23 |
STL 항목 1 : 적재적소에 알맞은 컨테이너를 사용하자. (0) | 2009.10.22 |
ㄱ. STL은 일반화에 기초를 두고 있어서 컨테이너도 일반화로 만들려고 하면 안 된다.
즉, 각각의 컨테이너에 공통적으로 있는 알고리즘은 사용 할 수 있지만, 각 컨테이너에 있는 좋은(?) 고유의 알고리즘은 사용 할 수 없다는 것.
ㄴ. typedef을 활용하여 좀 더 쓰기 편하게 하자.
ㄷ. 컨테이너를 보관하는 클래스를 만들자.
변경이 필요 할 경우도 있는데 이럴 경우 바로 사용하지 않고 클래스를 사용해서 관리 하면 좀 더 쉽게 보관 할 수 있다.
STL 항목 7 : new로 생성한 포인터의 컨테이너를 사용할 때에는 컨테이너가 소멸되기 전에 포인터를 delete하는 일을 잊지 말자. (0) | 2009.10.28 |
---|---|
STL 항목 5 : 단일 요소를 단위로 동작하는 멤버 함수보다 요소의 범위를 단위로 동작하는 멤버 함수가 더 낫다. (0) | 2009.10.27 |
STL 항목 4 : size()의 결과를 0과 비교할 생각이라면 차라리 empty를 호출하자. (0) | 2009.10.27 |
STL 항목 3 : 복사는 컨테이너 안의 객체에 맞게 비용은 최소화하며, 동작은 정확하게 하자. (1) | 2009.10.23 |
STL 항목 1 : 적재적소에 알맞은 컨테이너를 사용하자. (0) | 2009.10.22 |
ㄱ. 컨테이너 구분.
① 표준 시퀀스 컨테이너 ( vector, string, deque, list )
② 표준 연관 컨테이너 ( set, multiset, map, multimap )
③ 연속 메모리 컨테이너 ( vector, string, deque )
④ 노드 기반 컨테이너 ( set, multiset, map, multimap, list )
ㄴ. 알맞은 컨테이너 사용은 언제?
① 시퀀스 컨테이너 사용 할 시기
- 컨테이너의 아무 위치에 요소를 삽입 해야 한다면…
- 컨테이너 요소들의 순서 결정에 직접 관여 해야 한다면…
② 탐색 속도가 중요하면?
- 해쉬 컨테이너, 정렬된 vector 그리고 표준 연관 컨테이너의 순서대로 생각.
③ 반복자, 포인터, 참조자가 무효화 되는 일을 최소화 해야 하나?
- 노드 기반 컨테이너를 사용 : 노드 삽입과 삭제가 일어나도 기존의 반복자나 포인터 혹은 참조자가 무효화 되지 않음.
반면, 연속 메모리 컨테이너는 전체적인 메모리 재 할당이 빈번히 일어나기 때문에 반복자나 포인터, 참조자가 무효화 되기 쉽다.
④ 컨테이너 내의 데이터가 C의 데이터 타입과 메모리 배열 구조적으로 호환 되야 하면 vector뿐!
STL 항목 7 : new로 생성한 포인터의 컨테이너를 사용할 때에는 컨테이너가 소멸되기 전에 포인터를 delete하는 일을 잊지 말자. (0) | 2009.10.28 |
---|---|
STL 항목 5 : 단일 요소를 단위로 동작하는 멤버 함수보다 요소의 범위를 단위로 동작하는 멤버 함수가 더 낫다. (0) | 2009.10.27 |
STL 항목 4 : size()의 결과를 0과 비교할 생각이라면 차라리 empty를 호출하자. (0) | 2009.10.27 |
STL 항목 3 : 복사는 컨테이너 안의 객체에 맞게 비용은 최소화하며, 동작은 정확하게 하자. (1) | 2009.10.23 |
STL 항목 2 : “컨테이너에 독립적인 코드” 라는 환상을 조심하자. (0) | 2009.10.23 |
/// 웹브라우저를 나타내는 클래스. namespace WebBrowserStuff { class WebBrowser { public: void clearCache() { cout << "clearCache" << endl; } void clearHistory() { cout << "clearHistory" << endl; } void removeCookies() { cout << "removeCookies" << endl; } }; void clearAll(WebBrowser& const _WebBrowser) { _WebBrowser.clearCache(); _WebBrowser.clearHistory(); _WebBrowser.removeCookies(); } } /// 다른 헤더 파일에 정의 namespace WebBrowserStuff { /// 즐겨 찾기 관련 편의 함수들. } /// 다른 헤더 파일에 정의 namespace WebBrowserStuff { /// 쿠가 관련 편의 함수들. }
지연 평가(lazy evaluation) (0) | 2010.09.02 |
---|---|
사용자 정의 타입변환 함수에 대한 주의를 놓지 말자. (0) | 2009.10.07 |
컴파일러의 분석. (4) | 2009.07.31 |
상속 받는 기본 매개변수 값 변경 금지! (0) | 2009.07.29 |
인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하기. (0) | 2009.07.15 |
컴파일러가 사용할 수 있는 타입변환 함수는 2가지.
1. 단일
인자 생성자.
: 이런 생성자는 매개변수가 하나를 받도록 선언 되어 있든지, 매개변수가 여러 개인데 처음
것을 제외한 나머지가 모두 기본값을 갖도록 선언 되어 있는 것.
두가지 방법이 있음.
①
explicit 키워드를 사용.
② 두 번의 사용자 정의 변환.
// Test.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다. // #include "stdafx.h" template< class T > class Array { public: /// 클래스 내부에 클래스를 넣어서 Array에서만 사용한다는것을 강조. /// public에 선언 해서 다른 사람들도 사용할 수 있도록... class ArraySize { public: ArraySize(int numElements) : theSize(numElements) { } int size() const { return theSize; } private: int theSize; }; Array(int lowbound, int highBound) { } /// 단일 인자를 int 대신 ArraySize로 받음. Array(ArraySize _size) :m_ArraySize(_size) { } T& operator [] (int index) { } friend bool operator== (const Array< int >& lhs, const Array< int >& rhs); private: ArraySize m_ArraySize; }; bool operator== (const Array< int >& lhs, const Array< int >& rhs) { /// 비교후 리턴. return true; } int _tmain(int argc, _TCHAR* argv[]) { /// int 10을 ArraySize _size에 있는 단일 인자를 사용하여 /// 임시 _size에 넣고 임시 _size로 m_ArraySize를 초기화. Array< int > a(10); /// 이렇게 두번의 사용자 정의 변환을 하게 해서 /// 컴파일 에러가 나게 해서 오작동을 예방 할 수 있음. /// 모어 이펙티브 C++ 항목5. return 0; }
2. 암시적
타입변환 연산자
: 변환 함수 => 객체에서 하나의 수로 변환 할 때.
ex> operator double() const;
생성자를 사용하지 않음. 변환 함수를 이용.
변환 함수는 클래스의 메소드 여야 한다.
변환 함수는 리턴형을 가지면 안 된다.
변환 함수는 전달인자를 가지면 안 된다.
암시적 타입변환 연산자를 사용되면 프로그래머가 의도하지 않은 결과를 초래 할 수 있는데 이를 방지 하기 위해서 타입변환 함수를 직업
정의하고 호출하는 방법으로 하면 된다.
// test2.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다. // #include "stdafx.h" #include < iostream > using namespace std; class Rational { public: Rational(const int _iFint, const int _iSint) :m_iFint(_iFint), m_iSint(_iSint) { } // /// 변환 함수가 있을 경우. // /// 변환 함수. // operator double() const // { return static_cast< double >( static_cast< double >(m_iFint) / static_cast< double >(m_iSint) ); // } /// 변환 함수가 없을 경우. /// 변환 함수를 사용하지 않고 함수를 직접 만듬. double asDouble() const { return static_cast< double >( static_cast< double >(m_iFint) / static_cast< double >(m_iSint) ); } private: int m_iFint; int m_iSint; }; int _tmain(int argc, _TCHAR* argv[]) { Rational test(1, 2); /// 변환 함수가 있을 경우. /// operator <<를 작성 하지 않았는데 /// 암시적 타입변환으로 에러를 발생하지 않는다. /// 즉, 프로그래머가 의도하지 않은 결과를 만들 수 있음. // cout << "operator double() const : " << test << endl; /// 변환 함수를 없을 경우, /// 에러 발생. operator double() const가 있을 경우 됨. cout << "operator double() const : " << test << endl; /// 함수를 직접 호출로 의도 하지 않았던 잘못된 호출을 막을 수 있음. cout << "double asDouble() const : " << test.asDouble() << endl; return 0; }
지연 평가(lazy evaluation) (0) | 2010.09.02 |
---|---|
멤버 함수 보다는 비멤버 비프렌드 함수에 더 가깝게 쓰자. (0) | 2009.10.18 |
컴파일러의 분석. (4) | 2009.07.31 |
상속 받는 기본 매개변수 값 변경 금지! (0) | 2009.07.29 |
인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하기. (0) | 2009.07.15 |
#include "stdafx.h" #include < iostream > class CWidget {결론부터 말하면 첫번째 부분 widget1(1) 는 에러를 발생하지 않고 두번째 출력부분 widget2()는 에러를 발생한다.
public: CWidget() {} CWidget(const int _iIndex): m_iIndex(_iIndex) {} ~CWidget() {} void Show() { std::cout << "Index " << m_iIndex << ": CWidget 출력" << std::endl; } private: int m_iIndex; }; int _tmain(int argc, _TCHAR* argv[]) { CWidget widget1(1); widget1.Show(); CWidget widget2(); widget2.Show(); return 0; }
/// 보통 우리가 쓰는 함수 선언. int Test(const int _i); /// 위와 같은 것입니다. ()는 무시 됨. int Test(const int (_i));/// 매개 변수의 이름이 생략 되도 같은 것임.int Test(const int);위의 3가지 함수 선언은 똑같습니다.
/// Test2는 함수 포인터( 인자는 없고, int를 리턴하는 )를 매개변수로 받음. int Test2(int (*pFun)()); /// 위와 같음 pFun를 포인터로 인식 - C와 C++에서 유효한 문법임. int Test2(int pFun());/// 위와 역시 같음. 매개 변수의 이름이 생략 된 것임. int Test2(int ());역시 위의 3가지 함수 선언은 같습니다.
멤버 함수 보다는 비멤버 비프렌드 함수에 더 가깝게 쓰자. (0) | 2009.10.18 |
---|---|
사용자 정의 타입변환 함수에 대한 주의를 놓지 말자. (0) | 2009.10.07 |
상속 받는 기본 매개변수 값 변경 금지! (0) | 2009.07.29 |
인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하기. (0) | 2009.07.15 |
상속된 이름을 숨기는 일은 피하자. (4) | 2009.07.05 |