Consol out, Consol In, End line 



cout과 endl 직접 구현하는 예제.


이 예제는 cout<<"wer"<<12123<<endl; 이런 식으로 할 수가 없다 .그래서 이런 연산을 가능케 하기위해 바꿔야 한다.





cout은 ostream 클래스의 객체이다.


ostream은 이름공간 std 안에 선언되어 있다.




만약....

int main(void)

{

Point pos(3, 5);

cout<<pos<<endl;    //3, 5 출력.

...

}


이런 유형의 연산이 가능하게 하고자 한다.

이것이 가능하기 위해선 << 연산자가 오버로딩 되어 있어야 한다. 그리고 만약 멤버함수의 형태로 오버로딩을 한다면, 다음과 같이 해석이 가능해야 하고,

cout.operator<<(pos);


전역함수의 형태로 오버로딩을 한다면, 다음과 같이 해석이 가능해야 한다.

operator<<(cout, pos);


어떠한 방법을 선택해야 하는가...?

# 멤버함수에 의한 방법을 선택하면 cout 객체의 멤버함수를 하나 추가해야 하므로, ostream 클래스를 정정해야 한다. 그런데 이는 불가능한 방법이다.


# 전역함수에 의한 방법을 택하는 수밖에 없다. 때문에 전역함수로 정의해야 한다.

&ostream operator<<(ostream &os, const Point &pos) { ... }




'Programming > C++' 카테고리의 다른 글

디폴트 대입 연산자의 문제점  (0) 2012.09.11
대입 연산자의 오버로딩2  (0) 2012.09.10
교환법칙 문제의 해결  (0) 2012.08.28
전위증가와 후위증가의 구분  (0) 2012.08.27
단항 연산자의 오버로딩  (0) 2012.08.27
Posted by scii
:

교환법칙이란?


=> A + B 의 결과는 B + A 의 결과와 같음을 뜻한다. 즉, 연산자를 중시능로 한 피연산자의 위치는 연산의 결과에 아무런 영향을 미치지 않는다는 법칙이다.

대표적으로 교환법칙이 성립하는 연산으로는 곱셈연산과 덧셈연산이 있다.



연산자 오버로딩을 통해, 서로 다른 자료형의 두 데이터간의 연산을 가능하게하는 예제)



pos*3; 은 pos.operator*(3); 으로 해석이 된다.


위 예제에서 오버로딩 한 곱셈 연산자의 경우, Point 클래스의 멤버함수 형태로 정의했기 때문에 Point 객체가 * 연산자의 왼편에 와야 한다. 


교환법칙이 성립되려면 cpy = 3 * pos; 가 성립되어야 하는데 멤버함수로는 불가능하다.

하지만 전역함수로 하면 불가능이 가능해진다.




교환법칙의 성립을 위한 구현


교환법칙이 성립하도록하려면

cpy = 3 * pos;


전역함수의 형태로 곱셈 연산자를 오버로딩 하는 수 밖에 없다. 즉, 위의 문장이 다음과 같이 해석이 되도록 연산자를 오버로딩 해야 한다. 

cpy = operator*(3, pos);


이를 위해서는 operator* 함수를 다음과 같이 정의해야 한다. 


Point operator*(int times, Point& ref)

{

Point pos(ref.xpos*times, ref.ypos*times);

return pos;

}


그런데, 다음과 같이 3*pos 를 pos*3 이 되도록 바꾸는 형태로 오버로딩을 해도 된다. 


Point operator*(int times, Point& ref)

{

return ref*times;

}



교환법칙이 성립되면, 이렇듯 보다 자연스러운 연산문의 구성이 가능하다.

Posted by scii
:

++ 연사자와 -- 연산자는 피연산자의 위치에 따라서 의미가 달라진다.


# 연산자 오버로딩에서 ++ 연산자와 -- 연산자는 전위연산에 해당할까 후위연산에 해당할까?

=> 오버로딩 한 연산자를 호출하는 형태만 봐도, 전위증가, 전위감소 연산에 해당한다는 사실을 알 수 있다.


그렇다면, 후위증가, 후위감소 연산에 대한 연산자 오버로딩은 어떻게 해야 할까?

=> C++ 에서는 전위 및 후위 연산에 대한 해석방식에 대해 다음의 규칙을 정해놓고 있다. 


++pos                    ->                    pos.operator++();

pos++                    ->                    pos.operator++(int);


--pos                    ->                    pos.operator--();

pos--                    ->                    pos.operator--(int);


즉, 키워드 int 를 이용해서 후위연산에 대한 함수를 전위연산에 대한 함수와 구분하고 있다. 물론 여기서 사용된 int는 단지 후위연산을 구분하기 위한 목적으로 선택된 것일 뿐!! 아무 상관 없다.



#include <iostream>

using namespace std;


class Point

{

private:

int xpos, ypos;


public:

Point(int x = 0, int y = 0)

:xpos(x), ypos(y)

{ }


void Show() const

{

cout<<xpos<<", "<<ypos<<endl;

}


Point& operator++() //선위증가

{

xpos+=1;

ypos+=1;


return *this;

}


const Point operator++(int) //후위증가

{

const Point retObj(*this); //디폴트 복사생성자로 인해 멤버 대 멤버로 복사진행.

xpos+=1;

ypos+=1;


return retObj;

}


friend Point& operator--(Point &);

friend const Point operator--(Point &, int);

};


Point& operator--(Point &ref) //선위감소

{

ref.xpos-=1;

ref.ypos-=1;

return ref;

}


const Point operator--(Point &ref, int) //후위감소

{

const Point retObj(ref);

ref.xpos-=1;

ref.ypos-=1;


return retObj;

}


int main(void)

{

Point pos(5, 5);

++pos;


pos.Show();

--pos;

pos.Show();


(--(--pos)).Show();


Point cpy = pos++;

cpy.Show();

pos.Show();


cpy = pos--; //operator--(pos);

cpy.Show();

pos.Show();


// (cpy++)++; //컴파일 에러

return 0;

}





반환형에서의 const 선언과 const 객체


const Point retObj(*this);        //함수 내에서 retObj 의 변경을 막겠다라는 뜻!


# 반황형으로 선언된 const는 어떠한 의미를 지닐까?


=> operator-- 함수의 반환으로 인해서 생성되는 임시객체를 const 객체로 생성하겠다. 라는 의미가 된다.


ex) const Point pos(3, 4);

# pos 객체를 상수화해서 pos 객체에 저장된 값의 변경을 허용하지 않겠다는 뜻이다. 그래서 const객체인 pos를 대상으로는 const로 선언된 함수만 호출이 가능하다.

(( const 함수 내에서는 const 함수의 호출만 허용하도록 제한한다 )) => 즉, const 객체를 대상으로는 값의 변경능력을 지니는, const로 선언되지 않은 함수의 호출을 허용하지 않겠다는 뜻이다. 



 - 결론

# 이 예제에서 반환형으로 const를 선언한 이유!!!


=> 후위증가, 감소 연산은 전위증가, 감소와는 다르게 같은 라인에 2번이상 못쓴다. 이것이 C, C++의 연산 특성이다. 

그래서 이 특성을 그대로 반영하기 위하여 반환형에 const를 넣어준 것이다. 컴파일 에러가 발생할 수 있도록!!!

'Programming > C++' 카테고리의 다른 글

cout, cin 그리고 endl의 정체  (0) 2012.08.30
교환법칙 문제의 해결  (0) 2012.08.28
단항 연산자의 오버로딩  (0) 2012.08.27
연산자 오버로딩 (Operator Overloading)  (0) 2012.08.25
가상 상속 (Virtual Inheritance)  (0) 2012.08.22
Posted by scii
:

피연산자가 두 개인 이항 연산자와 피연산자가 한 개인 단항 연산자의 가장 큰 차이점은 피연산자의 개수이다.

그리고 이에 따른 연산자 오버로딩의 차이점은 매개변수의 개수에서 발견된다.


증가, 감소 연산자의 오버로딩



# this는 객체자신의 포인터 값을 의미하므로, *this는 객체 자신을 의미한다.


# ++(++pos); 는 소괄호 부분이 먼저 다음의 형태로 해석된다.

=> ++(pos.operator++()); 그 다음에 반환이 참조 값이므로 다음의 형태가 된다.

=> (pos의 참조 값).operator++();

# 그런데 pos의 참조 값을 대상으로 하는 연산은 pos 객체를 대상으로 하는 연산이기 때문에 결과적으로 위의 문장이 실행되면서 pos 객체의 멤버변수 값은 다시 1씩 증가한다.


# 정리하면, 위 예제의 operator++ 함수에서 객체 자신을 참조할 수 있는 참조 값을 반환하는 이유는 일반적인 ++ 연산자와 같은 형태의 연산이 가능하게 하기 위함이다.




Posted by scii
:

# C++ 에서는 함수뿐만 아니라 연산자도 오버로딩이 가능하다.

# 연산자 오버로딩은 C++ 을 이해하는데 매우 중요한 요소이다.


# 함수가 오버로딩 되면, 오버로딩 된 수만큼 다양한 기능을 제공하게 된다. 

즉, 이름은 하나이지만 기능은 여러 가지가 되는 셈이다.

마찬가지로 연산자의 오버로딩을 통해서, 기존에 존재하던 연산자의 기본 기능 이외에 다른 기능을 추가할 수 있다.




※ + 뿐만 아니라.. -, /, *, %로도 해보자~!!!


"p1 + p2 가 p1.operator+(p2) 의 다른 표현이다. "

즉, 약속된 변환의 규칙이다. 'operator' 키워드와 '연산자' 를 묶어서 함수의 이름을 정의하면, 함수의 이름을 이용한 함수의 호출뿐만 아니라, 연산자를 이용한 함수의 호출도 허용해준다. 


※ 연산자 오버로딩이란 C++ 이 우리에게 제시하는 하나의 약속에 지나지 않는다.


연산자를 오버로딩 한 함수도 const 로 선언이 가능하다. 


# operator+ 함수는 const로 선언을 하는 것이다 좋다. 덧셈연산이라는 것이 원래 연산의 대상이 되는 피연산자의 값을 변경하는 게 아니고, 새로운 연산의 결과를 만들어내는 것이기 때문이다.



연산자를 오버로딩 하는 두 가지 방법


1. 멤버함수에 의한 연산자 오버로딩.

2. 전역함수에 의한 연산자 오버로딩.


+ 연산자는 전역함수를 이용해서도 오버로딩이 가능하다. 그리고 이렇게 전역함수를 이용해서 오버로딩을 하면 p1 + p2 는 다음과 같이 해석이 된다.

operator+(p1, p2);

즉, 어떻게 오버로딩을 했느냐에 따라 해석하는 방법이 두 가지로 나뉘게 된다. 


※ 참고로, 동일한 자료형을 대상으로 + 연산자를 전역함수 기반으로, 그리고 멤버함수 기반으로 동시에 오버로딩 할 경우, 멤버함수 기반으로 오버로딩 된 함수가 전역함수 기반으로 오버로딩 된 함수보다 우선시되어 호출된다. 

단, 일부 컴파일러는 이러한 상황에서 컴파일 에러를 발생시키기도 하니, 이러한 상황은 가급적 만들지 않는 게 좋다. 



전역함수 기반의 연산자 오버로딩에 대한 일반적인 모델 예 &

friend 선언이 적절히 사용된 예


Point 클래스는 + 연산에 대해서 연산자 오버로딩이 되어 있구나를 생각할 수 있다.

이렇듯 Point 클래스에 삽입된 friend 선언으로 인해서, 이 클래스는 + 연산에 대해 오버로딩이 되어 있다는 정보를 쉽게 확인할 수 있다.


※ 객체지향에는 "전역(Global)"에 대한 개념이 존재하지 않는다. 다만 C++은 C 스타일의 코드구현이 가능한 언어이기 때문에 전역에 대한 개념이 여전히 존재한다. 

따라서 특별한 경우가 아니면, 멤버함수를 기반으로 연산자를 오버로딩 하는 게 낫다.


#include <iostream>

using namespace std;


class Point

{

private:

int xpos, ypos;


public:

Point(int x=0, int y=0)

:xpos(x), ypos(y)

{ }


void Show() const

{

cout<<xpos<<", "<<ypos<<endl;

}


Point& operator+=(const Point &ref)

{

xpos += ref.xpos; 

ypos += ref.ypos;

return *this;

}


Point& operator-=(const Point &ref)

{

xpos -= ref.xpos; 

ypos -= ref.ypos;

return *this;

}


friend Point operator-(const Point &, const Point &);

friend bool operator==(const Point &, const Point &);

friend bool operator!=(const Point &, const Point &);

};


Point operator-(const Point &ref1, const Point &ref2)

{

Point pos(ref1.xpos-ref2.xpos, ref1.ypos-ref2.ypos);

return pos;

}


bool operator==(const Point &ref1, const Point &ref2)

{

if(ref1.xpos == ref2.xpos && ref1.ypos == ref2.ypos)

return true;

else 

return false;

}


bool operator!=(const Point &ref1, const Point &ref2)

{

return !(ref1==ref2); //오버로딩 된 == 연산자를 호출하고 있다.

}


int main(void)

{

Point p1(20, 30);

Point p2(5, 7);

Point p3(5, 7);


(p1-p2).Show(); //객체를 반환하므로 함수호출 가능.

(p2+=p3).Show(); //반환형이 참조형이므로 함수호출 가능.


if(p2==p3) //operator==(p1, p2);랑 100% 동일한 문장.

cout<<"같음"<<endl;

else

cout<<"다름"<<endl;


(p2-p3).Show(); //반환형이 참조형이므로 함수호출 가능.


if(operator!=(p1, p2))         //p1 != p2; 랑 100% 동일한 문장.

cout<<"다름"<<endl;

else

cout<<"같음"<<endl;


return 0;

}





오버로딩이 불가능한 연산자의 종류


# C++ 의 모든 연산자들이 오버로딩의 대상이 되는 것은 아니다.

- 오버로딩이 불가능한 연산자들 -


.                                          멤버 접근 연산자

.*                                        멤버포인터 연산자

::                                        범위 지정 연산자

?:                                        조건 연산자

sizeof                                  바이트 단위 크기 계산

typeid                                  RTTI 관련 연산자

static_cast                           형변환 연산자

dynamic_cast                       형변환 연산자

const_cast                           형변환 연산자

reinterpret_cast                     형변환 연산자


이들 연산자에 대해서 오버로딩을 제한하는 이유는 C++ 의 문법규칙을 보존하기 위함이다.


※ 참고로, 위의 연산자들을 오버로딩 해야만 하는 상황이 딱히 존재하지 않다.



멤버함수 기반으로만 오버로딩이 가능한 연산자


- 멤버함수 기반으로만 오버로딩이 가능한 연산자들 - 


=                                        대입 연산자

()                                       함수 호출 연산자

[]                                      배열 접근 연산자(인덱스 연산자)

->                                      멤버 접근을 위한 포인터 연산자


이들은 객체를 대상으로 진행해야 의미가 통하는 연산자들이기 때문에, 멤버함수 기반으로만 연산자의 오버로딩을 허용한다.




연산자를 오버로딩 하는데 있어서의 주의사항


1. 본래의 의도를 벗어난 형태의 연산자 오버로딩은 좋지 않다.


# 연산자를 오버로딩 할 때에는 연산자의 본래 의도를 가급적 충실히 반영해서, 연산자의 오버로딩으로 인해서 발생하는 혼란스러운 부분을 최소화해야 한다.


2. 연산자의 우선순위와 결합성은 바뀌지 않는다.


# 연산자가 갖는 연산의 기능은 오버로딩 되어도, 연산자가 지니는 우선순위와 결합성은 그대로 따르게 되어 있다.


3. 매개변수의 디폴트 값 설정이 불가능하다.


# 연산자 오버로딩의 특성상 매개변수의 디폴트 값이 설정되면, 함수의 호출관계가 매우 불분명해진다. 따라서 매개변수의 디폴트 값 설정은 허용되지 않는다.


4. 연산자의 순수 기능까지 빼앗을 수는 없다.

int operator+(const int num1, const int num2)    //정의 불가능한 함수

{

 return num1 * num2;

}

# int 형 데이터의 + 연산은 이미 그 의미가 정해져 있다. 이것을 변경하는, 위와 같은 함수의 정의는 허용되지 않는다. 

이처럼 연산자의 기본 기능을 변경하는 형태의 연산자 오버로딩은 허용되지 않는다.




"연산자 오버로딩" 이라고 불리는 이유.


int num = 5+5;

Point p3 = p1 + p2;    //p1과 p2는 Point형 객체


함수가 오버로딩 되면 전달되는 인자에 따라서 호출되는 함수가 달라진다. 이와 유사하게, 위의 두 문장에서 보이듯이, 연산자가 오버로딩 되면, 피연산자의 종류에 따라서 연산의 방식이 달라진다. 그래서 연산자 오버로딩이라 불리는 것이다.

Posted by scii
:

12.08.20 MON

Houdini/Houdini etc 2012. 8. 23. 16:01 |

LookUp Table    ==    LUT (럿)

# 컬러 그래프가 들어왔을 때, 어떤 값을 곱해서 다른 그래프를 만드는 것!


※ 사람 눈은 감마 2.2 이다.


※ Linear WorkFlow

# 작업할 땐 감마 1.0에서 작업을 하고 마지막에 2.2로 맞추는 것!!


※ 감마 2.2에 가장 가까운 것이 "sRGB" 이다.


※ HDR의 장점.

# Reflection이 정말 잘 나온다!!


'Houdini > Houdini etc' 카테고리의 다른 글

Cross Product  (0) 2012.11.27
후디니 내에서 쓸 수 있는 함수를 만들 때, 기본적으로 알아야 할 것들  (0) 2012.11.20
짝수 공식  (0) 2012.05.16
$F, $FF 의 차이점  (0) 2012.05.04
참고 수식  (0) 2012.05.03
Posted by scii
:

12.08.20 MON

Houdini/CHOPs 2012. 8. 23. 15:50 |

◎ Area CHOP

# 앞에 것을(위의 노드) 더해서 그래프로 만든다.


※ relative start/End

# 상대적이다. 스타트의 상대적.

# 그렇게 잘 안씀.


◎ Slope CHOP

# velocity의 변화량을 가지고 작업할 때 좋다.




※ Merge Sop 에서 "Warning" 이 나오는 이유?


=> 각각의 노드에서 애트리뷰트가... 한쪽에는 있는데 다른 한쪽에는 애트리뷰트가 없을 때 "경고"가 뜬다.

없는 쪽에는 "Default 값" 이 들어가서 들어가도 되냐고 경고가 뜨는 것이다.

'Houdini > CHOPs' 카테고리의 다른 글

CHOPs 의 slope 노드를 이용  (0) 2013.01.27
chops의 lag를 이용한 애니메이션  (0) 2013.01.27
12.08.16 THU  (0) 2012.08.17
12.08.13 MON  (0) 2012.08.14
12.08.09 THU  (0) 2012.08.12
Posted by scii
:

※ 다중 상속을 하였을 때 함수 호출관계의 모호함은 다른 상황에서도 발생할 수 있다.


#include <iostream>

using namespace std;


class Base

{

public:

Base()

cout<<"Base Constructor"<<endl; 

}


void SimpleFunc()

cout<<"BaseOne"<<endl;

}

};


class MiddleDerived_1 :public  Base

{

public:

MiddleDerived_1()

:Base()

{

cout<<"MiddleDerived_1 Constructor"<<endl;

}


void MiddleFunc_1()

{

SimpleFunc();

cout<<"Middlederived_1"<<endl; 

}

};


class MiddleDerived_2 :public  Base

{

public:

MiddleDerived_2()

:Base()

{

cout<<"MiddleDerived_2 Constructor"<<endl;

}


void MiddleFunc_2()

{

SimpleFunc();

cout<<"Middlederived_2"<<endl; 

}

};


class LastDerived :public MiddleDerived_1, public MiddleDerived_2

{

public:

LastDerived()

:MiddleDerived_1(), MiddleDerived_2()

{

cout<<"LastDerived Constructor"<<endl;

}


void ComplexFunc()

{

MiddleFunc_1();

MiddleFunc_2();

MiddleDerived_2::SimpleFunc();    //이렇게 명시해주지않으면 에러발생.

}                                                       //왜냐면 어느클래스에서 쓸 것인지 모르기 때문.

};                                                              //현재 Base Class는 두개이다.


int main(void)

{

cout<<"객체생성 전 ... "<<endl;

LastDerived ldr;


cout<<"객체생성 후 ... "<<endl;

ldr.ComplexFunc();


return 0;

}



위 예제는 LastDerived 클래스가 Base 클래스를 간접적으로 두 번 상속하였다. 다중상속의 모호한 상황이다!!


이렇듯 하나의 객체 내에 두 개의 Base 클래스 멤버가 존재하기 때문에 complexFunc 함수 내에서 이름만 가지고 SimpleFunc 함수를 호출할 수는 없다.

이 경우에는 다음과 같이, 어느 클래스를 통해서 간접 상속한 Base 클래스의 멤버함수를 호출할 것인지 명시해야 한다. 


ex) MiddleDerived_1::SimpleFunc();

// MiddleDerived_1 클래스가 상속한 Base 클래스의 SimpleFunc 함수호출을 명령!


그런데 이러한 상황에서, Base 클래스의 멤버가 LastDerived 객체에 하나씩만 존재하는 것이 타당한 경우가 대부분이다. 

즉, Base 클래스를 딱 한번만 상속하게끔 하는 것이 더 현실적인 해결책이 될 수 있다.

그리고 이를 위한 문법이 바로 "가상 상속" 이다.


ex) class MiddleDerived_1 :virtual public Base

{    };


ex) class MiddleDerived_2 :virtual public Base

{    };






#include <iostream>

using namespace std;


class Base

{

public:

Base()

cout<<"Base Constructor"<<endl; 

}


void SimpleFunc()

cout<<"BaseOne"<<endl;

}

};


class MiddleDerived_1 :virtual public Base

{

public:

MiddleDerived_1()

:Base()

{

cout<<"MiddleDerived_1 Constructor"<<endl;

}


void MiddleFunc_1()

{

SimpleFunc();

cout<<"Middlederived_1"<<endl; 

}

};


class MiddleDerived_2 : virtual public  Base

{

public:

MiddleDerived_2()

:Base()

{

cout<<"MiddleDerived_2 Constructor"<<endl;

}


void MiddleFunc_2()

{

SimpleFunc();

cout<<"Middlederived_2"<<endl; 

}

};


class LastDerived :public MiddleDerived_1, public MiddleDerived_2

{

public:

LastDerived()

:MiddleDerived_1(), MiddleDerived_2()

{

cout<<"LastDerived Constructor"<<endl;

}


void ComplexFunc()

{

MiddleFunc_1();

MiddleFunc_2();

SimpleFunc();        //virtual inheritance(가상 상속) 을 해주었기때문에

}                                     //Base Class는 하나로 오버라이딩되어 명시해주지 않아도 된다.

};                                            //왜냐면, Base Class가 하나밖에 없기 때문.


int main(void)

{

cout<<"객체생성 전 ... "<<endl;

LastDerived ldr;


cout<<"객체생성 후 ... "<<endl;

ldr.ComplexFunc();


return 0;

}



이렇듯, 가상으로 Base 클래스를 상속하는 두 클래스를 다음과 같이 다중으로 상속하게 되면,


class LastDerived : public MiddleDerived_1, public MiddleDerived_2

{    };


LastDerived 객체 내에는 MiddleDerived_1 클래스와 MiddleDerived_2 클래스가 동시에 상속하는 Base 클래스의 멤버가 하나씩만 존재하게 된다.!!


- 위 예제에서 SimpleFunc 함수를 이름만 가지고 호출할 수 있을 것이다. 

그리고 예제의 실행결과를 보면 실제로 Base 클래스의 생성자가 한번만 호출되는 것을 확인할 수 있다.

만약에 가상 상속을 하지 않는다면, Base 클래스의 생성자는 두 번 호출된다.

Posted by scii
: