bool operator>( Fixed &fixed ) const;
const Fixed &max( const Fixed &a, const Fixed &b);
Canonical Form 이란 정해진 규칙을 정확히 따르며 개발하는 프로그래밍을 의마한다. 일종의 code convention 이라 볼수있다.
기본 생성자는 별도로 생성자를 구현하지 않을시 사용되는 생성자이다.
매개변수는 없고 멤버 변수가 0,NULL 등으로 초디화 된다.
ClassName::ClassName(const ClassName &ref)
{ ... }
복사 생성자는 같은 클래스의 다른 객체의 참조자(Reference)를 인자로 받아,
그 참조자를 이용해서 자신의 객체를 초기화하는 클래스함수.
기존 값을 복사하여 전달해주므로 const 로 받는다.
복사 생성자를 직접 명시하지 않은 경우 기본복사생성자가 자동으로 생성된다.
이런한 기본 복사생성자는 얕은 복사가 수행되며
만약 깊은 복사가 필요하다면 사용자가 직접 정의해야한다.
얕은 복사 (Shallow copy)
**깊은 복사 (Deep Copy)
C++ 에서는 함수의 이름이 같아도 매개변수가 다르면 선언 및 정의가 가능하다.
오버로드(Overload)같은 이름을 가진 복수의 객체가 각각 다른 기능을 하도록 만드는것이다.
객체끼리 연산자(+,-,*,/ 등)를 사용한 연산은 불가능 하기 때문에 이를 가능하게 하려면 사용자가 연산자 오버로딩을 통해 연산자의 기존 기능을 재정의 해야한다.
오버로딩 방법은 다음과 같이 operator 키워드 뒤에 오버로딩할 연산자를 붙여주면 된다
ClassName& ClassName::operaotr=(const ClassName& ref)
{ ... }
소멸자는 객체의 수명이 다했을때 객체를 제거하기 위한 목적으로 사용된다.
객체의 수명이 끝나면 컴파일러가 자동으로 소멸자 함수를 호출하게되고 클래스명 앞에 ~기호를 붙여 정의할 수있다.
Turn-in directory : ex00/
Files to turn in : Makefile, main.cpp, Fixed.{h, hpp}, Fixed.cpp
Forbidden functions : None
Fixed a;
Fixed b(a); // 복사 생성자
Fixed c;
c = b; // 대입 연산자 오버로딩;
/* 복사 생성자 */
Fixed::Fixed(const Fixed *fixed)
{
std::cout << "Copy constructor called" << std::endl;
this->fixed_point_value = fixed.fixed_point_value;
}
/* 대입 연산자 오버로딩 */
Fixed &Fixed::operator=(const Fixed &fixed)
{
std::cout << "Copy assignment operator called" << std::endl;
this->fixed_point_value = fixed.fixed_point_vlaue;
return *this;
}
Turn-in directory : ex01/
Files to turn in : Makefile, main.cpp, Fixed.{h, hpp}, Fixed.cpp
Allowed functions : roundf (from <cmath>)
컴퓨터 언어에서 데이터 타입은 항상 최대 길이가 고정되어 있다.
부호비트 : 양수면 0, 음수면 1
지수비트 : 정수부
가수비트 : 소수부
고정 소수점은 소수점이 붙어 있는 수를 2진수로 변환하는 과정에서, 10진수를 2진수로 바꾼 결과를 그대로 대입하는 방식이다.
고정 소수점은 구현하기 편리하지만 범위의 정밀도가 낮아 소규모 시스템에서 간혹 쓰인다.
부동 소수점은 고정 소수점과 다르게 정규화(Normaliztion)과정을 거친다.
이는 2진수로 변환시 정수부에 1만 남겨두고 나머지를 소수부에 넣는 방식이다. (1.xxx * 2^n 형태로 변환)
부호비트는 양수면 0, 음수면 1을 저장한다.
지수부 에는 2^n 에서 n 에 해당하는 지수를 넣게 되는데 그냥 넣는게 아니라 지우세 bias를 더한 값을 저장하게 된다.
bias 계산법은 2^(n-1) - 1 이고 32비트 기준 127 이다(2^(8-1) -1 = 127)
가수부 에는 소수값을 표현하고 나머지는 모두 0으로 채운다.
7.625 를 부동 소수점으로 나타내면 다음과 같다.
7.625 -> 111.101 -> 1.11101 * 2^2
지수는 2 이고 bias는 42비트 기준 127 이므로 지수부에는 2 + 127 = 129 가 변환되어 저장된다.
고정 소수값은 2진수 정수 혹은 부동 소수값의 소수 부분을 넣을 비트 수를 정해두고 표현 한느것이다.
과제에서 소수 부분을 넣을 비트(fractional bits)를 8로 정해주었기 때문에 32비트 체계에서는 앞의 24비트를 정수부분, 8 비트를 소수 부분으로 표현한다.
정수를 고정 소수값으로 표현하기 위해서는 앞으로 8비트만큼 << 비트 시프트 연산을 해서 8비트만큼의 빈 공간을 만들면 된다.
임의의 정수 n을 고정 소수값으로 변환하려면 n << 8
해주면 된다.
그래야 마지막 8칸은 n.00000000
와 같이 소수로 표현할수 있기 때문이다.
이경우는 특정 값을 고정 소수점으로 변환한 것을 다시 원위치 시키면 된다.
즉, 변환 하고자 하는 값을 n이라고 한다면 n >> 8
하면 된다.
정수와 동일하게 << 연산을 통해 Fractional Bits의 공간을 마련해 주면되지만 float 나 double과 같은 부동 소수점의 경우 직접적으로 시프트 연산이 불가능하기 때문에 간접적으로 해야한다.
또한 현재 고정 소수점으로 저장할 클래스의 변수 타입이 int형 이므로 일부 데이터가 누락 될 수있다.
그러므로 비교적 정확한 변환을 위해 허용 함수인 roundf 를 통해 반올림을 진행할 필요가 있다.
임의의 실수 f를 고정 소수값으로 변환 하려면 roundf(f *(1<<8))과 같이 변환 하면 된다.
이경우는 실수를 고정 소수값으로 변환 한것을 반대로하면 된다. 다만 해당 과제에서 고정 소수값에서 실수로의 변환은
부동 소수점 -> 고정 소수점 -> 부동 소수점
의 변환 방식을 의미한다.
그러므로 기존 클래스에 저장하고 있는 고정소수점을 float형으로 강제 형변환 하여 부동 소수점으로 변환한뒤,
고정 소수점의 Fractional Bits를 표현하기 위해 << 연산한 값을 다시 되돌리면 된다.
즉 변환 하고자 하는 값을 f라고 한다면 (float)f / (1 << 8)
과 같이 변환 하면 된다
cout 는 ostream의 객체이다. 그로므로 클래스를 cout 로 출력 하고 싶다면,
ostream클래스의 operator<< 를 재정의 하면된다.
과제에서 요구하는 operator<< 은 클래스에 저장된 고정 소수점 값을 toFloat함수를 통해 부동 소수점으로 출력 하는것이다.
float Fixed::toFloat(void) const
{
return ((float)_value / (1 << _bits));
}
std::ostream& operator<<(std::ostream& os, const Fixed& obj)
{
os << obj.toFloat();
return os;
}
std::ostream& operator<<(std::ostream&os, const Fixed &fixed);
ex01
그리고 하나 알아두어야 할 것은 아래와 같이 Fixed객체를 출력하는 방법을 알아야 한다.
Fixed a;
std::cout << "a is " << a << std::endl;
std::ostream 에 Fixed 객체를 파라미터로 가지는 operator<< 함수가 있어야 한다는말인데
하지만 표준 라이브러리를 직접 변경하는것은 불합리하기 때문에
외부에서 std::ostream& operator<<(const Fixed& fixed) 라는 함수를 재정의 해야한다.
Now we’re talking
Turn-in directory : ex02/
Files to turn in : Makefile, main.cpp, Fixed.{h, hpp}, Fixed.cpp
Allowed functions : roundf (from <cmath>)
fixed 클래스에 다음의 연산자 및 함수들을 public 멤버함수로 오버로드 해야한다.
operator++ (void); , operator--(void);
꼴로,operator++(int); , operator--(int);
꼴로 나누어 구분한다Fixed &operator++ (void);
Fixed operator--(void);
이 두개의 차이 반환 값이 참조 이냐 그냥이나의 차이#include <iostream>
int main( void ) {
Fixed a;
Fixed const b( Fixed( 5.05f ) * Fixed( 2 ) );
std::cout << a << std::endl;
std::cout << ++a << std::endl;
std::cout << a << std::endl;
std::cout << a++ << std::endl;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << Fixed::max( a, b ) << std::endl;
return 0;
}
Default constructor called // Fixed a;
Float constructor called // Fixed( 5.05f );
Int constructor called // Fixed( 2 )
Float constructor called // Fixed const b;
Destructor called // Fixed( 5. 05f ) 의 소멸자
Destructor called // Fixed( 2 ) 의 소멸자
0 // 기본소멸자로 생성된 a. 0으로 초기화 했던 값이 나옴
0.00390625 // ++a라고 1이 아니라 fixed_point_value를 ++하는 거기 때문에
// (1 / 2^8)한 값이 float형태로 출력되는 것.
0.00390625
Copy constructor called // 후위 증감 연산자에서는 복사 생성자가 호출됨
0.00390625 // 후위 증감 연산자이기 때문에 연산이 일어나기 전의 값이 리턴
Destructor called // 복사 생성자의 소멸자
0.0078125
10.1016
10.1016
Destructor called // b의 소멸자
Destructor called // a의 소멸
ex02
<friend 키워드>
통상적으로 자기 자신을 리턴하지 않는 이항 연산자들,
예를 들어 +,-,*,/ 들은 모두 외부 함수로 선언하는것이 원칙 입니다.
반대로 자기자신을 리턴하는 이항 연산자들,
예를들어 +=, -= 같은 애들은 모두 멤버함수로 선언하는것이 원칙!
멤버변수나 멤버함수 뿐만 아니라 객체에도 const 키워드를 사용할수 있다.
객체 생성시에 const 키워드를 사용하면
그 객체는 상수로 취급되어 초기화된 데이터 외의 다른 데이터로 변경할수 없다.
비교연산시 toFloat()로 변환하면 오차가 생긴 값을 비교하게 된다.
그래서 getRawBits() 로 비교를 해줘야 한다.
BSP
Turn-in directory : ex03/
Files to turn in : Makefile, main.cpp, Fixed.{h, hpp}, Fixed.cpp,
Point.{h, hpp}, Point.cpp, bsp.cpp
Allowed functions : roundf (from <cmath>)