[ Programming ]2010. 9. 2. 00:14
지연 평가? 요즘 주가를 올리고 있고 요즘 가장 이쁘다고 느끼는 티아라의 지연을 평가 하는것이 아니다 -_-
지연 평가란 진짜 필요해지기 전까지 미루고 미루는 것입니다.
지연 평가를 적용할 수 있는 분야는 일반적으로 네 가지로!!

1. 참조 카운팅(Reference Counting)
같은 값으로 선언 되어 있는 값이 존재한데 똑같은 값을 또 선언 할 필요가 없다. 이런 것을 카운터를 사용하여 하나만 선언 되게 하고 참조 하여 사용 하게 하는것!

2. 데이터 읽기와 쓰기를 구분하기
데이터를 읽기와 쓰기를 정확히 구분하는것

3. 지연 방식의 데이터 가져오기(Lazy Fetching)
한번에 모든 데이터를 가져 오는것이 아닌, 필요한 데이터만 가져오는 것!

4. 지형 방식의 표현식 평가(Lazy Expression Evaluation)
한마디로 쓸모 없는 계산을 하지 말라는 것이다. 예를 들면.... 앞에서 힘들게 계산 했는데... 그걸 사용 안하거나, 다른 값을 대입하는 경우... 그냥 글을 읽어 보면... 누가 그렇게 코딩 하냐고 하겠지만... 자세히 보면 은근히 많이 찾을수 있음.

Posted by 냉동

ㄱ.   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>

Posted by 냉동

ㄱ.   단일 요소 멤버 함수보다 범위 멤버 함수가 더 좋은 이유.

    범위 멤버 함수를 사용한 코드가 대개 짧다.

    범위 멤버 함수는 훨씬 명확하고 간결한 의미를 전달 한다.

ㄴ.   단일 요소 멤버 함수에서는 메모리 할당자의 요구 많음.
객체 복사도 빈번, 불필요한 연산도 자주 일어남
.
같은 일은 한다면, 범위 멤버 함수는 이 보다 효율적.

ㄷ.   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;
}
Posted by 냉동

empty는 모든 표준 컨테이너에 대해 상수 시간에 수행 하지만, 몇몇 제품에서 구현된 list 클래스에서 size가 선형 시간에 수행 되기 때문에 컨테이너가 비어 있는지를 확인 할 때 empty를 사용하여 비용?을 줄이자

Posted by 냉동

ㄱ.   STL의 방식은 복사되어 ( 컨데이너에 ) 들어가고, 복사 되어 나온다.
이런 복사는 해당 클래스의 복사용 멤버 함수를 사용 ( 복사 생성자, 복사 대입 연산자 ).

ㄴ.   STL의 기본 복사 방식으로 생기는 문제점.

    STL의 기본이 복사 방식으로 이루어 지기 때문에 데이터가 크면 클수록 메모리 사용량과 CPU 사용률은 더욱 높아 지는 문제점.

    상속된 객체의 경우 복사 동작이 제대로 이루어 지지 않는 형상 슬라이스.
기본 클래스의 컨테이너를 만들고 파생 클래스를 객체를 넣으면, 복사 과정에서 기본 생성자가 쓰이면서 파생 클래스 부분에 선언된 데이터는 잘려나감.

ㄷ.   해결 방안.
객체의 컨테이너를 만들지 않고, 포인터의 컨테이너를 만듬.

    속도가 빠름 포인터만 복사 하면 되기 때문

    정확 포인터를 이루는 비트를 복사 하기 때문.

    슬라이스 문제 해결 포인터를 복사 될 때 잘려나가는 데이터가 생기지 않음.


이렇게 포인터의 컨테이너를 만들면 인해 메모리 관리에 대한 문제점이 생기지만 스마트 포인터를 사용하면 해결 가능.

Posted by 냉동

ㄱ.   STL은 일반화에 기초를 두고 있어서 컨테이너도 일반화로 만들려고 하면 안 된다.
, 각각의 컨테이너에 공통적으로 있는 알고리즘은 사용 할 수 있지만, 각 컨테이너에 있는 좋은(?) 고유의 알고리즘은 사용 할 수 없다는 것.

ㄴ.    typedef을 활용하여 좀 더 쓰기 편하게 하자.

ㄷ.   컨테이너를 보관하는 클래스를 만들자.
변경이 필요 할 경우도 있는데 이럴 경우 바로 사용하지 않고 클래스를 사용해서 관리 하면 좀 더 쉽게 보관 할 수 있다.

Posted by 냉동

ㄱ.   컨테이너 구분.

    표준 시퀀스 컨테이너 ( vector, string, deque, list )

    표준 연관 컨테이너 ( set, multiset, map, multimap )

    연속 메모리 컨테이너 ( vector, string, deque )

    노드 기반 컨테이너 ( set, multiset, map, multimap, list )

ㄴ. 알맞은 컨테이너 사용은 언제?

   시퀀스 컨테이너 사용 할 시기
-
컨테이너의 아무 위치에 요소를 삽입 해야 한다면
-
컨테이너 요소들의 순서 결정에 직접 관여 해야 한다면

    탐색 속도가 중요하면?
-
해쉬 컨테이너, 정렬된 vector 그리고 표준 연관 컨테이너의 순서대로 생각.

    반복자, 포인터, 참조자가 무효화 되는 일을 최소화 해야 하나?
-
노드 기반 컨테이너를 사용 : 노드 삽입과 삭제가 일어나도 기존의 반복자나 포인터 혹은 참조자가 무효화 되지 않음.
반면, 연속 메모리 컨테이너는 전체적인 메모리 재 할당이 빈번히 일어나기 때문에 반복자나 포인터, 참조자가 무효화 되기 쉽다.

    컨테이너 내의 데이터가 C의 데이터 타입과 메모리 배열 구조적으로  호환 되야 하면 vector!

Posted by 냉동
[ Programming ]2009. 10. 18. 00:57
클래스를 만들고 사용하다 보면 여러개의 함수를 호출할때가 있다. 이런 경우 하나의 멤버 함수를 추가하고 필요한 여러개의 함수를 새로 만든 함수에서 호출하여 하나의 함수를 호출 하여 편하게 사용하는 경우가 종종있다.
이런 경우 멤버 함수 보다 비멤버 비프렌드 함수로 만드는 경우가 더 좋다.
  • 캡슐화가 더 좋다.
    - 멤버 변수를 private에 선언하면 접근 할수 있는 유일한 방법은 멤버 함수와 프렌드 함수이다. 즉 멤버 함수가 늘어 날수록 캡슐화가 낮아짐.
  • 컴파일 의존도를 낮출 수 있고 확장성이 좋다.
    - 네임스페이스를 이용. 네임스페이스는 클래스와 달리 나누어서 만들수 있어 세분화 하여 분리 하여 컴파일 의존도를 낮출수 있음. 또한 세분화 하여 확장성이 유용한다.
/// 웹브라우저를 나타내는 클래스.
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
{
	/// 쿠가 관련 편의 함수들.
}
Posted by 냉동
[ Programming ]2009. 10. 7. 20:34

컴파일러가 사용할 수 있는 타입변환 함수는 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;
}
Posted by 냉동
[ Programming ]2009. 7. 31. 17:27
내가 생각하는 것이 컴파일러가 분석하는 것과 똑같다면 아마도 난 지금쯤 C++의 고수?쯤 되어 있지 않을까 싶다. 멋진 게임을 만들고 있을 것이고 유능한 프로그래머가 되서 여기 저기 국내뿐만 아니라, 해외 여기 저기서 날 스카웃 할려고 노력 할것이다.
하지만, 현실은....-_-;; 내가 아는 것이 별로 없고 컴파일러의 분석을 제대로 파악 하지 못했기 때문에 항상 제자리 걸음이고 책을 늘 봐야 하는 현상에 있는 것이 현실인것 같다는 생각이 든다... 안되는건 알고 있었는데 설명을 하라고 하면.... "안되니깐요..." 라고 말을 할수가 없지 않은가!! 오늘 알게 됐다...
#include "stdafx.h"
#include < iostream >
class CWidget
{
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; }
결론부터 말하면 첫번째 부분 widget1(1) 는 에러를 발생하지 않고 두번째 출력부분 widget2()는 에러를 발생한다.
왜 그런가? 이유는 컴파일러의 분석이 내가 생각하는 것과 다르기 때문이다.
/// 보통 우리가 쓰는 함수 선언.
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가지 함수 선언은 같습니다.

즉, 매개 변수를 둘러싼 괄호는 무시 되어도 무방한 것이지만, 그냥 괄호는 함수의 매개 변수 리스트가 있음을 나타내는 것입니다. 그 자체가 함수의 포인터임을 알리는 것입니다.

이젠 에러가 나는 이유를 설명 할수 가 있는데, 내가 생각한것은 Widget w()는 인자값이 없는 기본 생성자를 통해서 객체를 생성 하려고 한것이지만, 컴파일러의 분석은 그게 아니란 말이죠.. 컴파일러는 단순히 Widget를 반환하는 매개변수가 없는 함수w를 선언한 건뿐입니다.

아... 이런것도 모르다니... 젠장
Posted by 냉동