~特殊な制御構造~
答えがわかった時点でreturnする
int Compare(int value1, int value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
}
return 0;
}
複数のエラー条件を確認する例
//ファイル名チェック
if (file.validName()) {
//ファイルが開けたかチェック
if (file.open()) {
//暗号キーが入力されているかチェック
if (encryptionKey.valid()) {
//暗号キーが正しいかチェック
if (file.decrypt(encryptionKey)) {
//正常時の処理
}
}
}
}
インデントが深すぎて読むのが苦痛・・・
ガード句を使って単純化しよう!!
if (!file.validName()) {
return;
}
if (!file.open()) {
return;
}
if (!encryptionKey.valid()) {
return;
}
if (!file.decrypt(encryptionKey)) {
return;
}
//正常時の処理が以下に続く
答えがわかった時点で処理を戻す
さらによくするなら、エラー内容をしっかり上位層に伝えてあげる
if (!file.validName()) {
return FILE_INVALID_NAME_ERROR;
}
if (!file.open()) {
return FILE_OPEN_ERROR;
}
if (!encryptionKey.valid()) {
return INVALID_ENCRYPTION_KEY;
}
if (!file.decrypt(encryptionKey)) {
return DECRYPTION_ERROR;
}
//正常時の処理が以下に続く
各関数のreturn文は最小限に抑える
?
早めに返せるものは返してしまってよいのでは?
そもそも関数の最後のreturnだけをみるパターンって
そんなにある?
|
--------
|
|
|
|
→
|
bool FindPathThroughMaze(Maze maze, Point position) {
//既に通った道は試さない
if (AlreadyTried(maze, position)) {
return false;
}
//この地点が出口であれば、成功を返す
if (ThisIsTheExit(maze, position)) {
return true;
}
//この地点をすでに通ったものとして記憶する
RememberPosition(maze, position);
//上下左右への経路を確認する
//いずれかの経路が成功すれば、終了する
if (MoveLeft(maze, position, &newPosition)) {
if (FindPathThroughMaze(maze, newPosition)) {
return true;
}
}
if (MoveUp(maze, position, &newPosition)) {
if (FindPathThroghMaze(maze, newPosition)) {
return true;
}
}
if (MoveDown(maze, position, &newPosition)) {
if (FindPathThroghMaze(maze, newPosition)) {
return true;
}
}
if (MoveRight(maze, position, &newPosition)) {
if (FindPathThroughMaze(maze, newPosition)) {
return true;
}
}
return false;
}
void RecursiveMethod(int* safetyCounter) {
if (*safetyCounter > SAFETY_LIMIT) {
return;
}
(*safetyCounter)++;
//何らかの処理
recursiveMethod(safetyCounter);
}
再帰は頭が痛くなるので使いたくないし読みたくない!!
エラーが発生したらリソースを解放する(goto使用例)
//複数のファイルを削除する
void DeleteFiles(ErrorCode* error_code) {
*error_code = ErrorCode.FILE_STATUS_SUCCESS;
std::list<File> file_list;
//削除するファイルを取得
MakeDeleteFileList(&file_list);
std::iterator it = file_list.begin();
while (it != file_list.end()) {
//ファイルオープンチェック
if (!OpenFile(*it)) {
*error_code = ErrorCode.FILE_STATUS_OPEN_ERROR;
goto FIN;
}
//ファイル上書きチェック
if (!OverwriteFile(*it)) {
*error_code = ErrorCode.FILE_STATUS_OVERWRITE_ERROR;
goto FIN;
}
//ファイル削除チェック
if (!DeleteFile(*it)) {
*error_code = ErrorCode.FILE_STATUS_DELETE_ERROR;
goto FIN;
}
++it;
}
FIN:
RemoveFileList(&file_list);
}
gotoを避ける為にネストする例
//複数ファイルを削除する
void DeleteFiles(ErrorCode* error_code) {
error_code = ErrorCode.FILE_STATUS_SUCCESS;
std::list<File> file_list;
//削除するファイルを取得
MakeDeleteFileList(&file_list);
std::iterator it = file_list.begin();
while (it != file_list.end() && *error_code == ErrorCode.FILE_STATUS_SUCCESS) {
if (OpenFile(*it)) {
if (OverwriteFile(*it)) {
if (!DeleteFile(*it)) {
*error_code = ErrorCode.FILE_STATUS_DELETE_ERROR;
}
} else {
*error_code = ErrorCode.FILE_STATUS_OVERWRITE_ERROR;
}
} else {
*error_code = ErrorCode.FILE_STATUS_OPEN_ERROR;
}
++it;
}
RemoveFileList(&file_list);
}
gotoを避ける為に状態変数を利用する例
//複数ファイルを削除する
void DeleteFiles(ErrorCode* error_code) {
error_code = ErrorCode.FILE_STATUS_SUCCESS;
std::list<File> file_list;
//削除するファイルを取得
MakeDeleteFileList(&file_list);
std::iterator it = file_list.begin();
while (it != file_list.end() && *error_code == ErrorCode.FILE_STATUS_SUCCESS) {
if (!OpenFile(*it)) {
*error_code = ErrorCode.FILE_STATUS_OPEN_ERROR;
}
if (*error_code == ErrorCode.FILE_STATUS_SUCCESS) {
if (!OverwriteFile(*it)) {
*error_code = ErrorCode.FILE_STATUS_OVERWRITE_ERROR;
}
}
if (*error_code == ErrorCode.FILE_STATUS_SUCCESS) {
if (!DeleteFile(*it)) {
*error_code = ErrorCode.FILE_STATUS_DELETE_ERROR;
}
}
++it;
}
RemoveFileList(&file_list);
}
try-finallyで書き直す例
//複数ファイルを削除する
void DeleteFiles(ErrorCode* error_code) {
error_code = ErrorCode.FILE_STATUS_SUCCESS;
std::list<File> file_list;
//削除するファイルを取得
MakeDeleteFileList(&file_list);
std::iterator it = file_list.begin();
try {
while (it != file_list.end()) {
OpenFile(*it);
OverwriteFile(*it);
DeleteFile(*it);
++it;
}
} finally {
RemoveFileList(&file_list);
}
}
どれも一長一短・・・
使うかどうか悩むことが今後あるかすら怪しい
新しく書くコードにgotoは使わない
かつてgoto文は良い考えとみなされていた
ソフトウェア開発はプログラマがコードを使ってできることを
制限することによって大きく前進してきた
今後、ここで登場した制御構造は
ゴミ箱に捨てられる運命にあるのではないか