第2章:課題の実装
はじめに
本章では、CPP Module 02の各演習を実装します。
---
1. ex00: My First Class in Orthodox Canonical Form
1.1 課題の要件
- Orthodox Canonical Formに従ったFixedクラスを作成
- privateメンバ:
_rawBits(int)と_fractionalBits(static const int = 8) - デフォルトコンストラクタ、コピーコンストラクタ、代入演算子、デストラクタ
getRawBitsとsetRawBitsメンバ関数
1.2 実装
// Fixed.hpp
#ifndef FIXED_HPP
#define FIXED_HPP
#include <iostream>
class Fixed {
private:
int _rawBits;
static const int _fractionalBits = 8;
public:
Fixed();
Fixed(const Fixed& other);
Fixed& operator=(const Fixed& other);
~Fixed();
int getRawBits() const;
void setRawBits(int const raw);
};
#endif
// Fixed.cpp
#include "Fixed.hpp"
Fixed::Fixed() : _rawBits(0) {
std::cout << "Default constructor called" << std::endl;
}
Fixed::Fixed(const Fixed& other) {
std::cout << "Copy constructor called" << std::endl;
*this = other;
}
Fixed& Fixed::operator=(const Fixed& other) {
std::cout << "Copy assignment operator called" << std::endl;
if (this != &other) {
this->_rawBits = other.getRawBits();
}
return *this;
}
Fixed::~Fixed() {
std::cout << "Destructor called" << std::endl;
}
int Fixed::getRawBits() const {
std::cout << "getRawBits member function called" << std::endl;
return this->_rawBits;
}
void Fixed::setRawBits(int const raw) {
this->_rawBits = raw;
}
// main.cpp
#include "Fixed.hpp"
int main() {
Fixed a;
Fixed b(a);
Fixed c;
c = b;
std::cout << a.getRawBits() << std::endl;
std::cout << b.getRawBits() << std::endl;
std::cout << c.getRawBits() << std::endl;
return 0;
}
1.3 期待される出力
Default constructor called
Copy constructor called
Copy assignment operator called
getRawBits member function called
Default constructor called
Copy assignment operator called
getRawBits member function called
getRawBits member function called
0
getRawBits member function called
0
getRawBits member function called
0
Destructor called
Destructor called
Destructor called
---
2. ex01: Towards a more useful fixed-point number class
2.1 課題の要件
toFloatとtoIntメンバ関数を追加<<演算子のオーバーロード2.2 実装
// Fixed.hpp
#ifndef FIXED_HPP
#define FIXED_HPP
#include <iostream>
#include <cmath>
class Fixed {
private:
int _rawBits;
static const int _fractionalBits = 8;
public:
Fixed();
Fixed(const int n);
Fixed(const float n);
Fixed(const Fixed& other);
Fixed& operator=(const Fixed& other);
~Fixed();
int getRawBits() const;
void setRawBits(int const raw);
float toFloat() const;
int toInt() const;
};
std::ostream& operator<<(std::ostream& os, const Fixed& fixed);
#endif
// Fixed.cpp
#include "Fixed.hpp"
Fixed::Fixed() : _rawBits(0) {
std::cout << "Default constructor called" << std::endl;
}
Fixed::Fixed(const int n) : _rawBits(n << _fractionalBits) {
std::cout << "Int constructor called" << std::endl;
}
Fixed::Fixed(const float n) : _rawBits(roundf(n * (1 << _fractionalBits))) {
std::cout << "Float constructor called" << std::endl;
}
Fixed::Fixed(const Fixed& other) {
std::cout << "Copy constructor called" << std::endl;
*this = other;
}
Fixed& Fixed::operator=(const Fixed& other) {
std::cout << "Copy assignment operator called" << std::endl;
if (this != &other) {
this->_rawBits = other.getRawBits();
}
return *this;
}
Fixed::~Fixed() {
std::cout << "Destructor called" << std::endl;
}
int Fixed::getRawBits() const {
return this->_rawBits;
}
void Fixed::setRawBits(int const raw) {
this->_rawBits = raw;
}
float Fixed::toFloat() const {
return static_cast<float>(this->_rawBits) / (1 << _fractionalBits);
}
int Fixed::toInt() const {
return this->_rawBits >> _fractionalBits;
}
std::ostream& operator<<(std::ostream& os, const Fixed& fixed) {
os << fixed.toFloat();
return os;
}
// main.cpp
#include "Fixed.hpp"
int main() {
Fixed a;
Fixed const b(10);
Fixed const c(42.42f);
Fixed const d(b);
a = Fixed(1234.4321f);
std::cout << "a is " << a << std::endl;
std::cout << "b is " << b << std::endl;
std::cout << "c is " << c << std::endl;
std::cout << "d is " << d << std::endl;
std::cout << "a is " << a.toInt() << " as integer" << std::endl;
std::cout << "b is " << b.toInt() << " as integer" << std::endl;
std::cout << "c is " << c.toInt() << " as integer" << std::endl;
std::cout << "d is " << d.toInt() << " as integer" << std::endl;
return 0;
}
2.3 固定小数点変換の詳細
整数から固定小数点数:
整数 10 を固定小数点数に変換:
10 << 8 = 10 * 256 = 2560
内部表現: 2560 = 0x0A00
浮動小数点数から固定小数点数:
42.42 を固定小数点数に変換:
42.42 * 256 = 10859.52
roundf(10859.52) = 10860
内部表現: 10860 = 0x2A6C
固定小数点数から浮動小数点数:
内部表現 10860 を浮動小数点数に変換:
10860 / 256.0 = 42.421875
(元の42.42との誤差は精度による)
2.4 期待される出力
Default constructor called
Int constructor called
Float constructor called
Copy constructor called
Copy assignment operator called
Float constructor called
Copy assignment operator called
Destructor called
a is 1234.43
b is 10
c is 42.4219
d is 10
a is 1234 as integer
b is 10 as integer
c is 42 as integer
d is 10 as integer
Destructor called
Destructor called
Destructor called
Destructor called
---
3. ex02: Now we're talking
3.1 課題の要件
- 6つの比較演算子:
>,<,>=,<=,==,!= - 4つの算術演算子:
+,-,*,/ - 4つのインクリメント/デクリメント演算子: 前置/後置
++,-- - 静的メンバ関数:
min,max
3.2 実装
// Fixed.hpp
#ifndef FIXED_HPP
#define FIXED_HPP
#include <iostream>
#include <cmath>
class Fixed {
private:
int _rawBits;
static const int _fractionalBits = 8;
public:
// Orthodox Canonical Form
Fixed();
Fixed(const int n);
Fixed(const float n);
Fixed(const Fixed& other);
Fixed& operator=(const Fixed& other);
~Fixed();
// Getter/Setter
int getRawBits() const;
void setRawBits(int const raw);
// Conversion
float toFloat() const;
int toInt() const;
// Comparison operators
bool operator>(const Fixed& rhs) const;
bool operator<(const Fixed& rhs) const;
bool operator>=(const Fixed& rhs) const;
bool operator<=(const Fixed& rhs) const;
bool operator==(const Fixed& rhs) const;
bool operator!=(const Fixed& rhs) const;
// Arithmetic operators
Fixed operator+(const Fixed& rhs) const;
Fixed operator-(const Fixed& rhs) const;
Fixed operator*(const Fixed& rhs) const;
Fixed operator/(const Fixed& rhs) const;
// Increment/Decrement operators
Fixed& operator++(); // pre-increment
Fixed operator++(int); // post-increment
Fixed& operator--(); // pre-decrement
Fixed operator--(int); // post-decrement
// Static member functions
static Fixed& min(Fixed& a, Fixed& b);
static const Fixed& min(const Fixed& a, const Fixed& b);
static Fixed& max(Fixed& a, Fixed& b);
static const Fixed& max(const Fixed& a, const Fixed& b);
};
std::ostream& operator<<(std::ostream& os, const Fixed& fixed);
#endif
// Fixed.cpp
#include "Fixed.hpp"
// ===== Orthodox Canonical Form =====
Fixed::Fixed() : _rawBits(0) {}
Fixed::Fixed(const int n) : _rawBits(n << _fractionalBits) {}
Fixed::Fixed(const float n) : _rawBits(roundf(n * (1 << _fractionalBits))) {}
Fixed::Fixed(const Fixed& other) : _rawBits(other._rawBits) {}
Fixed& Fixed::operator=(const Fixed& other) {
if (this != &other) {
this->_rawBits = other._rawBits;
}
return *this;
}
Fixed::~Fixed() {}
// ===== Getter/Setter =====
int Fixed::getRawBits() const {
return this->_rawBits;
}
void Fixed::setRawBits(int const raw) {
this->_rawBits = raw;
}
// ===== Conversion =====
float Fixed::toFloat() const {
return static_cast<float>(this->_rawBits) / (1 << _fractionalBits);
}
int Fixed::toInt() const {
return this->_rawBits >> _fractionalBits;
}
// ===== Comparison operators =====
bool Fixed::operator>(const Fixed& rhs) const {
return this->_rawBits > rhs._rawBits;
}
bool Fixed::operator<(const Fixed& rhs) const {
return this->_rawBits < rhs._rawBits;
}
bool Fixed::operator>=(const Fixed& rhs) const {
return this->_rawBits >= rhs._rawBits;
}
bool Fixed::operator<=(const Fixed& rhs) const {
return this->_rawBits <= rhs._rawBits;
}
bool Fixed::operator==(const Fixed& rhs) const {
return this->_rawBits == rhs._rawBits;
}
bool Fixed::operator!=(const Fixed& rhs) const {
return this->_rawBits != rhs._rawBits;
}
// ===== Arithmetic operators =====
Fixed Fixed::operator+(const Fixed& rhs) const {
Fixed result;
result.setRawBits(this->_rawBits + rhs._rawBits);
return result;
}
Fixed Fixed::operator-(const Fixed& rhs) const {
Fixed result;
result.setRawBits(this->_rawBits - rhs._rawBits);
return result;
}
Fixed Fixed::operator*(const Fixed& rhs) const {
Fixed result;
// 乗算後、小数ビット分をシフト
result.setRawBits((this->_rawBits * rhs._rawBits) >> _fractionalBits);
return result;
}
Fixed Fixed::operator/(const Fixed& rhs) const {
Fixed result;
// 除算前に小数ビット分をシフト
result.setRawBits((this->_rawBits << _fractionalBits) / rhs._rawBits);
return result;
}
// ===== Increment/Decrement operators =====
Fixed& Fixed::operator++() {
this->_rawBits++;
return *this;
}
Fixed Fixed::operator++(int) {
Fixed temp(*this);
++(*this);
return temp;
}
Fixed& Fixed::operator--() {
this->_rawBits--;
return *this;
}
Fixed Fixed::operator--(int) {
Fixed temp(*this);
--(*this);
return temp;
}
// ===== Static member functions =====
Fixed& Fixed::min(Fixed& a, Fixed& b) {
return (a < b) ? a : b;
}
const Fixed& Fixed::min(const Fixed& a, const Fixed& b) {
return (a < b) ? a : b;
}
Fixed& Fixed::max(Fixed& a, Fixed& b) {
return (a > b) ? a : b;
}
const Fixed& Fixed::max(const Fixed& a, const Fixed& b) {
return (a > b) ? a : b;
}
// ===== Stream operator =====
std::ostream& operator<<(std::ostream& os, const Fixed& fixed) {
os << fixed.toFloat();
return os;
}
// main.cpp
#include "Fixed.hpp"
int main() {
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;
}
3.3 演算の詳細
乗算の仕組み:
a = 5.05 (内部: 1293)
b = 2 (内部: 512)
1293 * 512 = 662016
662016 >> 8 = 2586
2586 / 256.0 = 10.1016 ≈ 10.1
インクリメントの仕組み:
++操作は_rawBitsを1増加
1 / 256 = 0.00390625
これが固定小数点数の最小単位
3.4 期待される出力
0
0.00390625
0.00390625
0.00390625
0.0078125
10.1016
10.1016
---
4. ex03: BSP(Bonus)
4.1 課題の要件
- Pointクラスを作成
bsp関数で点が三角形内にあるかを判定
4.2 実装
// Point.hpp
#ifndef POINT_HPP
#define POINT_HPP
#include "Fixed.hpp"
class Point {
private:
Fixed const _x;
Fixed const _y;
public:
Point();
Point(const float x, const float y);
Point(const Point& other);
Point& operator=(const Point& other);
~Point();
Fixed getX() const;
Fixed getY() const;
};
bool bsp(Point const a, Point const b, Point const c, Point const point);
#endif
// Point.cpp
#include "Point.hpp"
Point::Point() : _x(0), _y(0) {}
Point::Point(const float x, const float y) : _x(x), _y(y) {}
Point::Point(const Point& other) : _x(other._x), _y(other._y) {}
Point& Point::operator=(const Point& other) {
// constメンバのため代入不可
// 何もしない
(void)other;
return *this;
}
Point::~Point() {}
Fixed Point::getX() const {
return this->_x;
}
Fixed Point::getY() const {
return this->_y;
}
// bsp.cpp
#include "Point.hpp"
// 外積の符号を計算
static Fixed sign(Point const& p1, Point const& p2, Point const& p3) {
return (p1.getX() - p3.getX()) * (p2.getY() - p3.getY())
- (p2.getX() - p3.getX()) * (p1.getY() - p3.getY());
}
bool bsp(Point const a, Point const b, Point const c, Point const point) {
Fixed d1 = sign(point, a, b);
Fixed d2 = sign(point, b, c);
Fixed d3 = sign(point, c, a);
bool has_neg = (d1 < Fixed(0)) || (d2 < Fixed(0)) || (d3 < Fixed(0));
bool has_pos = (d1 > Fixed(0)) || (d2 > Fixed(0)) || (d3 > Fixed(0));
// 点が辺上にある場合は除外(d1, d2, d3のいずれかが0)
if (d1 == Fixed(0) || d2 == Fixed(0) || d3 == Fixed(0)) {
return false;
}
return !(has_neg && has_pos);
}
// main.cpp
#include "Point.hpp"
#include <iostream>
int main() {
// 三角形: (0,0), (10,0), (5,10)
Point a(0.0f, 0.0f);
Point b(10.0f, 0.0f);
Point c(5.0f, 10.0f);
// テストケース
Point inside(5.0f, 5.0f); // 内部
Point outside(0.0f, 5.0f); // 外部
Point on_edge(5.0f, 0.0f); // 辺上
Point on_vertex(0.0f, 0.0f); // 頂点上
std::cout << "Inside: " << (bsp(a, b, c, inside) ? "true" : "false") << std::endl;
std::cout << "Outside: " << (bsp(a, b, c, outside) ? "true" : "false") << std::endl;
std::cout << "On edge: " << (bsp(a, b, c, on_edge) ? "true" : "false") << std::endl;
std::cout << "On vertex: " << (bsp(a, b, c, on_vertex) ? "true" : "false") << std::endl;
return 0;
}
4.3 アルゴリズムの解説
外積を使った判定:
三角形ABCを反時計回りに辿ると:
- 点Pが内部にある場合、AB×AP、BC×BP、CA×CPはすべて同じ符号
- 点Pが外部にある場合、符号が混在
三角形 ABC:
A(0,0), B(10,0), C(5,10)
点P(5,5)について:
AB = (10,0), AP = (5,5)
AB × AP = 10*5 - 0*5 = 50 > 0
BC = (-5,10), BP = (-5,5)
BC × BP = -5*5 - 10*(-5) = -25 + 50 = 25 > 0
CA = (-5,-10), CP = (0,-5)
CA × CP = -5*(-5) - (-10)*0 = 25 > 0
すべて正 → 点Pは三角形内
4.4 期待される出力
Inside: true
Outside: false
On edge: false
On vertex: false
---
まとめ
本章で実装した内容: