C++によるオブジェクト指向プログラミング
コンストラクタはオブジェクトの記憶領域が確保された直後に実行され、ディストラクタはオブジェクトの記憶領域が解放された直後に実行されます。どちらも自動的に実行されます。また、コンストラクタの中で変数を new した場合は、通常ディストラクタの中でその変数を delete します。
注)C++では構造体もクラスも値型(value type)です。
注)A a; クラスAのデフォルトコンストラクタが定義されていれば、aはデフォルトコンストラクタで初期化されます。
注)デフォルト引数(default parameter)のデフォルト値(default value)は、関数のプロトタイプ宣言で設定します。
注)デフォルトコンストラクタとは、引数がないか、全ての引数にデフォルト値が設定されているコンストラクタです。
注)引数無しのコンストラクタと全ての引数がデフォルト引数であるコンストラクタを同時に定義するとエラーになります。
注)static const の整数型メンバー変数を除いて、メンバー変数を宣言と同時に初期化しないこと! → 初期化してもよい!
オブジェクトの初期化と代入
注)C++では初期化の=と代入の=の違いに注意する必要があります。
class.cpp
#include <iostream>
class Robot {
public:
double x = 1, y = 1;
};
int main() {
Robot robot;
std::cout << robot.x << std::endl;
std::cout << robot.y << std::endl;
}
object.cpp
#include <iostream>
class Robot {
double x, y;
public:
Robot();
Robot(double, double);
void set(double, double);
double getX() const;
double getY() const;
};
//メンバー初期化リストを使った引数無しコンストラクタ&デフォルトコンストラクタ
//Robot::Robot() : x(0), y(0) {}
//引数無しコンストラクタ&デフォルトコンストラクタ
Robot::Robot() {
set(0, 0);
}
//メンバー初期化リストを使った引数付きコンストラクタ
//Robot::Robot(double x, double y) : x(x), y(y) {}
//引数付きコンストラクタ
Robot::Robot(double x, double y) {
set(x, y);
}
void Robot::set(double x, double y) {
this->x = x;
this->y = y;
}
double Robot::getX() const {
return x;
}
double Robot::getY() const {
return y;
}
int main() {
//デフォルトコンストラクタによる初期化
Robot robot;
std::cout << robot.getX() << std::endl;
std::cout << robot.getY() << std::endl;
//一時オブジェクトの代入
robot = Robot();
robot = Robot(0, 0);
//デフォルトコピーコンストラクタによる初期化
Robot robot0(robot);
//デフォルト代入演算子によるオブジェクトの代入
robot0 = robot;
//引数無しコンストラクタによる初期化
Robot robot1(); // -> NG
auto robot2 = Robot();
//引数付きコンストラクタによる初期化
Robot robot3(3, 3);
auto robot4 = Robot(4, 4);
}
robot.hpp
#pragma once
#include <iostream>
class Robot {
double x, y;
public:
static int nRobots;
//default constructor with default parameters
Robot(double x = 0, double y = 0) : x(x), y(y) {}
Robot(const Robot& robot) {
x = robot.x;
y = robot.y;
}
Robot& operator=(const Robot& robot) {
x = robot.x;
y = robot.y;
return *this;
}
virtual ~Robot() {}
void set(double x, double y) {
this->x = x;
this->y = y;
}
double getX() const {
return x;
}
double getY() const {
return y;
}
void move(double dx, double dy) {
x += dx;
y += dy;
}
void print() const {
using namespace std;
cout << "(x, y) = " << "(" << x << ", " << y << ")" << endl;
}
};
int Robot::nRobots = 0;
main.cpp
#include "robot.hpp"
int main() {
//Robot robot0;
//Robot robot0(1);
//Robot robot0(1, 1);
auto robot0 = Robot();
robot0.print();
Robot robot1(robot0);
robot1 = robot0;
robot1.move(1, 1);
robot1.print();
auto robot2 = new Robot;
robot2->move(2, 2);
robot2->print();
delete robot2;
//g++ OK, clang++ NG
Robot::nRobots = 10;
Robot robot3[Robot::nRobots];
for (int i = 0; i < Robot::nRobots; i++) {
robot3[i].set(0, 0);
robot3[i].move(3, 3);
robot3[i].print();
}
Robot::nRobots = 10;
auto robot4 = new Robot[Robot::nRobots];
for (int i = 0; i < Robot::nRobots; i++) {
robot4[i].set(0, 0);
robot4[i].move(4, 4);
robot4[i].print();
}
delete[] robot4;
}
build.bat
set path=d:\sdk\msys64\mingw64\bin;%path%; g++ -std=c++17 -O3 -pedantic-errors -s -Wall -static *.cpp -o main.exe strip main.exe
robot.hpp
#pragma once
class Robot {
double x, y;
public:
static int nRobots;
Robot(double x = 0, double y = 0);
Robot(const Robot&);
Robot& operator=(const Robot&);
virtual ~Robot();
void set(double, double);
double getX() const;
double getY() const;
void move(double, double);
void print() const;
};
robot.cpp
#include "robot.hpp"
#include <iostream>
int Robot::nRobots = 0;
/* member initialization list
Robot::Robot() : x(0), y(0) {}
Robot::Robot(double x, double y) : x(x), y(y) {}
*/
Robot::Robot(double x, double y) {
this->x = x;
this->y = y;
}
//copy constructor
Robot::Robot(const Robot& robot) {
x = robot.x;
y = robot.y;
}
//assignment operator overloading
Robot& Robot::operator=(const Robot& robot) {
x = robot.x;
y = robot.y;
return *this;
}
Robot::~Robot() {}
void Robot::set(double x, double y) {
this->x = x;
this->y = y;
}
double Robot::getX() const {
return x;
}
double Robot::getY() const {
return y;
}
void Robot::move(double dx, double dy) {
x += dx;
y += dy;
}
void Robot::print() const {
using namespace std;
cout << "(x, y) = " << "(" << x << ", " << y << ")" << endl;
}
main.cpp
#include "robot.hpp"
int main() {
//Robot robot0;
//Robot robot0(1);
//Robot robot0(1, 1);
auto robot0 = Robot();
robot0.print();
Robot robot1(robot0);
robot1 = robot0;
robot1.move(1, 1);
robot1.print();
auto robot2 = new Robot;
robot2->move(2, 2);
robot2->print();
delete robot2;
//g++ OK, clang++ OK
const int n = 10;
Robot robot3[n];
for (int i = 0; i < n; i++) {
robot3[i].set(0, 0);
robot3[i].move(3, 3);
robot3[i].print();
}
Robot::nRobots = 10;
auto robot4 = new Robot[Robot::nRobots];
for (int i = 0; i < Robot::nRobots; i++) {
robot4[i].set(0, 0);
robot4[i].move(4, 4);
robot4[i].print();
}
delete[] robot4;
}
build.bat
set path=d:\sdk\msys64\mingw64\bin;%path%; g++ -std=c++17 -O3 -pedantic-errors -s -Wall -c robot.cpp -o robot.o g++ -std=c++17 -O3 -pedantic-errors -s -Wall -static robot.o main.cpp -o main.exe strip main.exe
オブジェクトをメンバーに持つクラス
member_object.cpp
#include <iostream>
class X {
public:
double x;
X(double x = 0);
};
X::X(double x) : x(x) {}
class Y {
public:
double y;
Y(double y = 0);
};
Y::Y(double y) : y(y) {}
class Robot {
public:
X x;
Y y;
//引数付きコンストラクタ&デフォルトコンストラクタ
Robot(double x = 1, double y = 1);
};
//引数付きコンストラクタを使ったメンバー初期化リスト
Robot::Robot(double x, double y) : x(x), y(y) {}
/*
Robot::Robot(double x, double y) {
this->x = X(x);
this->y = Y(y);
}
*/
int main() {
X x;
Y y;
std::cout << x.x << std::endl;
std::cout << y.y << std::endl;
//Robot robot; // -> OK
//Robot robot(); // -> NG
//Robot robot(1, 1); // -> OK
//auto robot = Robot(); // -> NG
auto robot = Robot(1, 1);
std::cout << robot.x.x << std::endl;
std::cout << robot.y.y << std::endl;
}
クラスの継承
inheritance.cpp
#include <iostream>
class X {
public:
double x;
X(double x = 0);
};
X::X(double x) : x(x) {}
class Y {
public:
double y;
Y(double y = 0);
};
Y::Y(double y) : y(y) {}
class Robot : public X, public Y {
public:
//引数付きコンストラクタ&デフォルトコンストラクタ
Robot(double x = 1, double y = 1);
};
//スーパークラスの引数付きコンストラクタを使ったメンバー初期化リスト
Robot::Robot(double x, double y) : X(x), Y(y) {}
/*
Robot::Robot(double x, double y) {
X::x = x;
Y::y = y;
}
*/
int main() {
X x;
Y y;
std::cout << x.x << std::endl;
std::cout << y.y << std::endl;
//Robot robot; // -> OK
//Robot robot(); // -> NG
//Robot robot(1, 1); // -> OK
//auto robot = Robot(); // -> NG
auto robot = Robot(1, 1);
std::cout << robot.x << std::endl;
std::cout << robot.y << std::endl;
}
array.hpp
#pragma once
class Array {
public:
static double common;
static const long length = 10;
private:
double element[length];
public:
ArragetY();
double& operator[](long);
};
array.cpp
#include "array.hpp"
#include <cassert>
double Array::common = 0;
Array::ArragetY() {
if (length > 0) {
for (long i = 0; i < length; i++) {
element[i] = 0;
}
}
}
auto Array::operator[](long index) -> double& {
assert(0 <= index && index < length);
return element[index];
}
main.cpp(オブジェクトを static data area に割り当てるプログラム)
#include "array.hpp"
#include <iostream>
auto array = ArragetY();
//auto array = new ArragetY(); // -> NG
auto main() -> int {
for (long i = 0; i < Array::length; i++) {
array[i] = i;
std::cout << array[i] << std::endl;
}
std::cout << Array::common << std::endl;
}
main.cpp(オブジェクトを stack area に割り当てるプログラム)
#include "array.hpp"
#include <iostream>
auto main() -> int {
auto array = ArragetY();
for (long i = 0; i < Array::length; i++) {
array[i] = i;
std::cout << array[i] << std::endl;
}
std::cout << Array::common << std::endl;
}
main.cpp(オブジェクトを heap area に割り当てるプログラム)
#include "array.hpp"
#include <iostream>
auto main() -> int {
auto array = new ArragetY();
for (long i = 0; i < Array::length; i++) {
(*array)[i] = i;
std::cout << (*array)[i] << std::endl;
}
delete array;
std::cout << Array::common << std::endl;
}
build.bat
set path=d:\sdk\msys64\mingw64\bin;%path%; clang++ -std=c++17 -O3 -pedantic-errors -s -Wall -static *.cpp -o main.exe strip main.exe
注)Windowsのstack領域のサイズは、2MB。
参考サイト
- 記憶領域
- Google:インクルードガード
- オブジェクト指向プログラミングとは結局なんなのか
- C++ クラス設計に関するノート
- 戻り値が参照の関数
- Google:C++ 初期化 代入
- C++における初期化と代入の違い
- C++:value initialization
- C++の初期化
- C++:初期化の種類
- C++:一時オブジェクトの代入
- C++:メンバーイニシャライザー
- C++:クラスのメンバー変数の初期化
- C++:コピーコンストラクターと代入演算子
- C++:デフォルトコピーコンストラクタとデフォルト代入演算子