入門06

C++言語によるプログラミング入門6
クラスを作ろう(補足)

 こんにちは。前回はNekoというクラスを作ってみました。宿題の「犬」はできましたか?(これは最後に考えます。)
 今回は、クラスNekoを、もう少し書き直してみます。プログラムの実行そのものに変化はなく、ちょっと足踏みな感じがするかもしれませんが、 一気に新しいneko3.cppまで読んでみてください。
 前回は次のようなクラスNeko(猫)を書いてみました。

class Neko
{
private:
    string name;
public:
    Neko(string s){
        name = s;
    }
    void naku(){
        cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
    }
};

 説明は長かったですが、コード量はどうということはありません。わかりにくくてもプログラム(前回のneko.cpp)をコンパイルし実行してみてください。だんだんわかってくると思います。わかってしまえば簡単なのです。

 このクラスについて、いくつか補足があります。

●用語について
 クラス内にある変数は、メンバ変数とかデータメンバといいます。また、クラス内の関数はメンバ関数といいます。要するに、クラス内のものはメンバなのですね。

●コンストラクタの定義について
 Nekoのコンストラクタは、

    Neko(string s){
        name = s;
    }

です。その処理内容は「引数の値(sで表される)を、メンバ変数nameに代入する」ですね。つまり、オブジェクト生成時に行われることは、「オブジェクト内で、まず、nameという変数を作って、それから、その変数に値を代入する」ということになります。実は、「いったん変数を作って、それからその変数に値を代入する」のではなく「変数を作るときに値を入れる」ということもできます。それは

    Neko(string s) : name(s){}

とするのです。ここで「name(s)」が、「変数nameを作るときにsの値を格納する」という意味になるのです。このようにnameにデータを与えると、コンストラクタはほかにすることがなくなるので、中カッコの中は空でよくなります。この方が、ひと手間少ないですし、これからは、このように書くことにします。

●オブジェクトのデータを変えないメンバ関数について
 オブジェクトのデータを変えないメンバ関数には、後にconstというキーワードを付けるのがよいとされています。これは、「この関数は実行してもデータを変えないよ」という印です。今のところ、あってもなくてもよいようですが、いずれ「間違いのない(間違いの少ない)プログラム」書くのに重要になってきます。そこで、今後は、オブジェクトのデータを変えない関数には、constを付けることにします。
 すると、nakuは、オブジェクトのデータ(今はnameのことです)を使っているけど、変更はしていないので、constを付けるべき関数ということになります。そのように書くと、nakuの定義は次のようになります。

    void naku() const{
        cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
    }

 なお、C++では、意味のある部分の途中でなければ、自由に改行や空白をいれてよいことになっています。(実は、改行も空白の一種と考えられます。一方、日本語の空白(全角の空白)は、空白ではなく文字と解釈されるので、注意してください。)
 そのため、nakuは

    void naku() const{ cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl; }

などと書くこともできます。しかし、なるべく見やすいように書いた方がよいでしょう。

●private:について
 クラス定義の1番上のprivate:は省略してもよいことになっています。これがなくても、public:の上にあるものは、プライベート(非公開)になるのです。そこで、これも省略することにします。

 ところで、どうしてデータを非公開にするのだろうと思っているかもしれません。これは、初心者にはなかなか難しい話題です。簡単に言うと、「間違ってデータを変更できないようにする」ためなのです。実は、長いプログラムでは、変更すべきでないデータを間違って変更してしまうというミスをしがちです。これを防ぐことができるのです。
 neko.cppは短いプログラムですが、長いプログラムだと想像してみてください。Nekoのnameには、オブジェクトを生成するときにだけ、コンストラクタを通して値(名前)を与えることができます。nakuはnameの値を変更しないので、結局、nameはオブジェクト生成後には変更できないデータになっているのです。もちろん、変更したい場合には、変更する方法が必要になる(作ることもできます)わけですが、変更の必要がなければ、その手段もないように作っておく方が間違いが少ないのです。つまり、このようにデータを守ることができるんだ、と思ってください。(コード例を一番最後にお見せします。)

 以上をまとめると、Nekoというクラスは次のように書けることになります。

class Neko
{
    string name;
public:
    Neko(string s) : name(s){}
    void naku() const{
        cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
    }
};

 ふうーーーっ。
 やっと終わりかと思ったら、もう1つあります。実は、コンストラクタやメンバ関数の処理内容が長い場合、その定義は、クラス定義の外に書くのが普通なのです。

 クラス定義の中にメンバ関数の定義を書くと、それはコンパイラに「できれば、関数を使う場所それぞれに、同じ処理を書き込んでくれ」と要求することになるのです。
 ちょっとわかりにくいですね。つまり「ある関数を5箇所で使う場合なら、その5箇所全部に直接関数の処理を書き込んでほしい。10箇所なら10箇所全部に」と要求することになるのです(そうでない場合はすぐに説明します)。このように「使われる場所」全部にその内容が書き込まれる関数をインライン関数などといいます。この言葉を使えば、「クラス定義の中にメンバ関数を書くと、コンパイラに、その関数をインライン化するよう要求することになる」と言えます。
 インライン関数(の処理)は、「使われる場所」すべてに書き込まれることになるので、インライン関数を使うと実行可能ファイルが大きくなります。そのため、処理が長い関数はインライン化しないのが、普通です。(コンパイラにインライン化を要求しても、コンパイラの判断でインライン化しない場合もあります。)
 そのような場合、クラス定義の中には、「こういう関数があるよ」という宣言だけ残し、その関数の定義はクラス定義の外に書くのです。そうすると、コンパイラにインライン化を要求しないことになるのです。
 ところで、インラインでない関数では処理がどう行われるか、説明していませんでした。どうなるかというと、どこかにひとつだけ「関数の処理」が書かれ、「(関数が)使われる場所」では、その「関数の処理」が呼び出され使われることになるのです。したがって、インラインでない関数を使うのには手間が少しかかるのです。そのため、インライン関数より実行時間がかかりますが、「関数の処理」は一箇所に書かれているだけなので、実行可能ファイルは小さくできます。

 たとえば、Nekoのコンストラクタもnakuの処理もそれほど大きくないですが、特に、インライン化する必要もないので、クラスの外に書くとすると、次のようになります。

class Neko
{
    string name;
public:
    Neko(string s);
    void naku() const;
};

Neko::Neko(string s) : name(s){}

void Neko::naku() const{
    cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
}

 まずクラス定義の中を見てください。「Neko();」が「コンストラクタがあるよ」、「void naku() const;」が「nakuという関数があるよ」という宣言なのです。これは、定義部分をとっぱらって、「;」をつけただけですね。
 クラス定義の下にあるのが、クラス定義の外にあるコンストラクタとnakuの定義です。
 ここでNeko::Nekoと書いてあるのが、コンストラクタです。冗長な感じがしますが、クラス定義の外ではコンストラクタをこのように書くのです。一般に、「Neko::」は「クラスNekoの」という意味です。したがって、「Neko::Neko」は「NekoというクラスのNekoという関数(コンストラクタ)」という意味になるのです。
 また、クラス定義の外では、nakuはNeko::nakuと書きます。これは、「Nekoというクラスのnakuという関数」という意味ですね。クラス定義の外では、クラス名をはっきり書かなければ、どのクラスのメンバ関数かわからなくなってしまうので、「Neko::」が必要なのです。(「Neko::」を書かなければ、クラスに属さない関数とみなされてしまいます。)

 長い長い補足でした。書き直したNekoで前回のプログラムneko2.cppを書き直してみましょう。プログラム名はneko3.cppにします。

//neko3.cpp
#include <iostream>
#include <string>
using namespace std;

class Neko
{
    string name;
public:
    Neko(string s);
    void naku() const;
};

Neko::Neko(string s) : name(s){}

void Neko::naku() const{
    cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
}

int main()
{
    string s;
    cout<<"猫を生成します。名前を入力してください。"<<endl;
    cin>>s;

    Neko dora(s);  //コンストラクタが実行され、文字列sの名前のdoraが生成される
    cout<<"あなたの名づけた猫がメモリ上に生成されました。"<<endl;
    cout<<"猫が鳴きます。"<<endl;

    dora.naku();   //doraに対してnakuを実行
}

 mainは前回と同じなので、実行の様子も前回と同じになります。

Fig.1 neko3.exeの実行画面

 これだけでは面白くないので、前回の宿題・「犬」のクラスを、今回の書き方で書いてみましょう。

 ただ、「書き方」についてあと1つコメントがあります。Nekoのコンストラクタの宣言にはsという変数を書きましたが、定義がここにない場合、省略してもよいことになっています。そこに定義がないので、どうせそこでは使わないからです。その場合、コンストラクタの宣言は「Neko(string);」となります。(もちろん、定義ではsを使っているので、クラス定義の外の方でsを省略することはできません。)犬のクラスInuでは、そのように書いてみます。

//inu.cpp
#include <iostream>
#include <string>
using namespace std;

class Inu
{
    string name;
public:
    Inu(string);   //「Inu(string s);」のsを省略した
    void naku() const;
};

Inu::Inu(string s) : name(s){}

void Inu::naku() const{
    cout<<"わん。僕は"<<name<<"だ。"<<endl;
}

int main()
{
    string s;
    cout<<"犬を生成します。名前を入力してください。"<<endl;
    cin>>s;

    Inu dog(s); 
    cout<<"あなたの名づけた犬がメモリ上に生成されました。"<<endl;
    cout<<"犬が鳴きます。"<<endl;

    dog.naku();
}

Fig.2 犬だってできる
 


 最後に、private:(省略されていても、一番上にあると考えます)の意味について、実際のコードで試しながら説明したいと思います。まだ興味のわかない人はななめ読みくらいにしてください。
 たとえば、上のneko3.cppで、mainの中の「dora.naku();」の直前に「dora.name = "太郎";」というコードを間違って書いてしまったとしましょう。「dora.name」と書くと、それは「doraのname」という意味になるのです。しかし、Nekoの定義では、nameをprivateにしたのでした。すると、privateなメンバ変数(つまり非公開のメンバ変数)を、クラスの外で変更しようとしているので、コンパイル時にエラーになるのです。(mainは、Nekoクラスの外であることに注意してください。)

//neko3x.cpp
#include <iostream>
#include <string>
using namespace std;

class Neko
{
    string name;
public:
    Neko(string s);   //「Neko(string);」でも可
    void naku() const;
};

Neko::Neko(string s) : name(s){}

void Neko::naku() const{
    cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
}

int main()
{
    string s;
    cout<<"猫を生成します。名前を入力してください。"<<endl;
    cin>>s;

    Neko dora(s);   //コンストラクタが実行され、文字列sの名前のdoraが生成される
    cout<<"あなたの名づけた猫がメモリ上に生成されました。"<<endl;
    cout<<"猫が鳴きます。"<<endl;

    dora.name = "太郎";    //エラーになる 
    dora.naku();
}

Fig.3 privateのものに直接アクセスするコードはコンパイルエラーになる(BorlandC++の例)

 しかし、Nekoを

class Neko
{
public:
    string name;     //public:の下に持ってきた
    Neko(string s);  //「Neko(string);」でも可
    void naku() const;
};

と変えると、nameはpublicになります。すると、このプログラムはコンパイルエラーではなくなるのです。もちろん、実行もできます。すると次のようになります。

Fig.4 nameをpublicにするとコンパイル・実行できる

 「nameの値を簡単に変えられるので、こっちのほうが便利じゃん」と思う人も多いようです。しかし、データを簡単に(直接)変更できてしまうのは危険なのです。プログラマがまちがって、「dora.name = "太郎";」のようにして、値を変更してしまうかもしれないからです。
 意図しないで「dora.name = "太郎"; 」なんて書いたりしないと思うかもしれませんが、長いプログラムだとそうでもありません。あるいは、そこまでハッキリしたコードではないところで、実質そのようなことをしてしまうのです。すると、「あれ、猫の名前をどう入力してもみんな太郎になるぞ。どうしてだ???」ということになるかもしれないのです。
 初心者はコンパイルエラーを嫌がりますが、コンパイルエラーは利用できるのです。つまり、nameをprivateにしておけば(publicの上に書いておけば)、間違ってnameを変更してしまうコードに対して、コンパイラがエラーを出してくれるのです。すると、プログラマは、その間違いにすぐ気が付くわけです。
 実際、恐ろしいのは実行時のエラーではないでしょうか。しかし、nameをpublicにしておくと、あとで実行時のエラーになるかもしれないのです。
 メンバ変数は、特別な理由がない限り、privateにしましょう。


 トップページにも書きましたが、細部にこだわるより、全体の流れを見てください。neko3.cppを書き換えてinu.cppができたなら、これまでのところは、わかっていることになるのです。
 もっと根本的に「クラスが何の役に立つのか」と思い始めたかもしれません。これは、おいおい説明していくつもりです。


補足(はじめは読み飛ばしてください)

 neko3.cppで、Nekoクラスを

class Neko
{
private:
    string name;
public:
    Neko(string s) : name(s){}
    void naku(){
        cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
    }
};

のように書くと、コンパイラが警告を出すかもしれません。たとえば、Borland C++ 5.6では、コンストラクタに関して

「値でクラスを渡す引数を持つ関数はインライン展開されない」

という警告が出ます。クラス定義内のコンストラクタの定義が

    Neko(string s){
        name = s;
    }

でも同じです。これは、「このコンストラクタはインラインにできない」という意味です。一般に、警告は重要です。が、今の段階で、この警告は気にしなくてもよいと思います。この警告をなくす1つの方法は、neko3.cppのようにコンストラクタの定義を外に書くというものです。また、参照(リファレンス)というものを使う方法もあります。この警告の意味は、「参照」というものがわかってはじめてわかるのです。気になる人は、「C++入門」を一通り読み終えてから、C++入門補足3を読んでみてください。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源
大学生在线租房平台管理系统按照操作主体分为管理员和用户。管理员的功能包括报修管理、报修评价管理、字典管理、房东管理、房屋管理、房屋收藏管理、房屋留言管理、房屋租赁管理、租房论坛管理、公告信息管理、留言板管理、用户管理、管理员管理。用户的功能等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 大学生在线租房平台管理系统可以提高大学生在线租房平台信息管理问题的解决效率,优化大学生在线租房平台信息处理流程,保证大学生在线租房平台信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理大学生在线租房平台信息,包括房屋管理,培训管理,报修管理,薪资管理等,可以管理公告。 房屋管理界面,管理员在房屋管理界面中可以对界面中显示,可以对房屋信息的房屋状态进行查看,可以添加新的房屋信息等。报修管理界面,管理员在报修管理界面中查看报修种类信息,报修描述信息,新增报修信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值