【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