【C++言語入門】 第18回 修飾子


constとかのキーワードのことですわね
const
constですわね
constについては、基本的に内容の変更を行わない、もしくは行えないことを示唆しますわ
変数の変更は禁止です
constの基本的な使い方は、「変数の内容の変更を禁止する」使い方ですわ
const double kPI = 3.14159265358979323846;
double型の変数"kPI"に対してconstで値の変更を禁止することで定数の役割としていますわ
constを付加するのもこれにあたりますわ
Circleクラスのコピーコンストラクタや代入演算子のオーバーライドが判りやすいと思いますわ
class Circle {
:
:
Circle(const Circle& c) : Circle(c.diameter_) {
memcpy_s(pmessage_, kMessageSize, c.pmessage_, kMessageSize);
}
:
:
Circle& operator=(const Circle& c) {
diameter_ = c.diameter_;
memcpy_s(pmessage_, kMessageSize, c.pmessage_, kMessageSize);
return *this;
}
};
constが付加されているので"c"の内容の変更はできませんわ
ポインタにconst
constを付加すると、一般的にはポインタが指すメモリ領域の値の変更が禁止されますわ
int a = 10;
const int* pa = &a;
*pa = 0; // NG
*pa = 0;のような代入によって"a"の値を変更することはできませんわ
int a = 10;
int b = 5;
int* const pa = &a;
*pa = 0; // OK
pa = &b; // NG
constが変数名の前に付加されているのだ
pa = &b;のように、ポインタの指す位置を変更することができなくなるのか...
*pa = 0;のようにポインタが指すメモリ領域の値の変更はOKとなりますわ
メンバ変数の変更は禁止です
constを付加しますわ
型 メソッド名(引数...) const;
constを付加した場合には、定義にもconstを付加する必要がありますわ
constを付加したメソッドでは、メソッド内でメンバ変数の内容が変わるような処理は禁止となりますわ
public:に指定されているメンバ変数でも変更禁止なのか?
const指定されたメソッド以外は呼び出し禁止となりますわ
Circleクラスを例に書き換えてみましょう
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
const double kPI = 3.14159265358979323846;
const int kMessageSize = 50;
/// @brief 円
class Circle {
double diameter_; // 直径
char* pmessage_;
public:
Circle(double diameter) : diameter_(diameter), pmessage_(nullptr) {
pmessage_ = new char[kMessageSize];
}
virtual ~Circle() {
delete[] pmessage_;
pmessage_ = nullptr;
}
double Diameter() const { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
virtual double Area() const {
double radius = Diameter() / 2.0;
double a = radius * radius * kPI;
return a;
}
virtual const char* Message() const;
};
#endif // CIRCLE_H
#include "circle.h"
const char* Circle::Message() const {
double area = Area();
sprintf_s(pmessage_, kMessageSize, "円の面積は%fです。", area);
return pmessage_;
}
#include <iostream>
#include "circle.h"
int main(int argc, char* argv[]) {
Circle c(10.0);
std::cout << "円の直径は" << c.Diameter() << "cmです。" << std::endl;
std::cout << c.Message() << std::endl;
return 0;
}

Messageメソッドにconstが付加されているのだ
Messageメソッド内からAreaメソッドやDiameterゲッターを呼び出しているのだ
constを付加しないとエラーとなりますわ
constを取り除いてみましょう
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
const double kPI = 3.14159265358979323846;
const int kMessageSize = 50;
/// @brief 円
class Circle {
double diameter_; // 直径
char* pmessage_;
public:
Circle(double diameter) : diameter_(diameter), pmessage_(nullptr) {
pmessage_ = new char[kMessageSize];
}
~Circle() {
delete[] pmessage_;
pmessage_ = nullptr;
}
double Diameter() { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
double Area() {
double radius = Diameter() / 2.0;
double a = radius * radius * kPI;
return a;
}
const char* Message() const;
};
#endif // CIRCLE_H

constを付加しなくても、メソッド自体はエラーとはなりませんし、問題なく動作しますわ
- コンパイラが最適化をし易くなる
- メソッドを呼ぶ際にプログラマが副作用を気にせず安心して使えるようになる
-
constを指定したインスタンスや引数から呼び出せるようになる
constを付加していないメソッドは、constを指定されたインスタンスや引数から呼び出せませんわ
メッセージの変更
Messageメソッド内ではsprintf_sでpmessage_内の文字列を変更しています
Messageメソッドはconstが指定されているのに、メンバ変数の内容が書き換えられているのは、おかしいと思われるかもしれません
ただMessageメソッド中ではpmessage_の値そのものが変更されていません
pmessage_の指すメモリ領域の内容が変わっているのです
少しややこしくて判りにくいのですが、「constを使っているから変更できない」と油断していると、知らないうちに内容を変更してしまっていることもあるので注意が必要です
noexcept
noexceptですわね
noexcept?
noexceptを付加するのですわ
型 関数名(引数...) noexcept { 処理 };
noexceptを付加した場合には、定義にもnoexceptを付加する必要がありますわ
noexceptを付加したらどうなるのだ?
noexceptを付加すると、どのようなメリットがあるのだ?
noexceptが付加された場合には、関数やメソッドから例外が投げられないことが保証されますわ
try~catchで処理する手間が省けますわね
noexceptが付加された関数やメソッドの内部で、例外が発生した場合はどうするのだ?
try~catchを使って例外を捕捉して処理すればいいのですわ
CircleクラスのMessageメソッドにnoexceptを付加してみましょう
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
const double kPI = 3.14159265358979323846;
const int kMessageSize = 50;
/// @brief 円
class Circle {
double diameter_; // 直径
char* pmessage_;
public:
Circle(double diameter) : diameter_(diameter), pmessage_(nullptr) {
pmessage_ = new char[kMessageSize];
}
~Circle() {
std::cout << "デストラクタが呼ばれました" << std::endl;
delete[] pmessage_;
}
double Diameter() const { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
double Area() const {
double radius = Diameter() / 2.0;
double a = radius * radius * kPI;
return a;
}
const char* Message() const noexcept;
};
#endif // CIRCLE_H
#include "circle.h"
const char* Circle::Message() const noexcept {
double area = Area();
sprintf_s(pmessage_, kMessageSize, "円の面積は%fです。", area);
return pmessage_;
}

noexceptを付加しているのだ
constとnoexceptの両方を指定しているのだ
noexceptをconstの前に配置するとエラーになるので注意して下さいね
throw()
関数やメソッドから投げられる可能性のある例外を指定する修飾子はあるのでしょうか
以前のC++言語の仕様ではthrowキーワードを使って、投げられる可能性のある例外を指定することができました
型 関数名(引数...) throw(例外の型...) { 処理 }
例外の型を列挙することで、どのような例外を処理すれば良いかが一目瞭然でした
ただ、最近のC++言語の仕様では、このような書き方はできなくなり、例外がない場合のみnoexceptを指定するように変更されました
結果として現在は、どのような例外が発生するかはドキュメントを頼る状況になっています
ですので、関数やメソッドに対するコメントの重要性は非常にに高まっています
まとめ
overrideやfinalという 修飾子 についてお話ししますわね
Discussion