2006年01月31日

第13章 constオブジェクト

今回は、constオブジェクトについて解説します。 constとは、読んで字のごとく「定数」という意味です。これは、勝手に値を変更できないことを意味しています。したがって、オブジェクトをconstで宣言するとデータメンバは、一切変更できなくなります。それどころかメンバ関数も一切呼び出せなくなります。もしかすると、メンバ関数が値を書き換えようとするかもしれないからです。
エー、そりゃ大変だな・・・
ま、例題プログラムを見てみましょう。

#include class Test { public: int a; Test(); ~Test(); int show(); }; Test::Test(void) { cout << "数値を入力-- "; cin >> a; } Test::~Test() { cout << "デストラクタが呼ばれました。\n"; } int Test::show(void) { cout << "[show] a = " << a << endl; return 0; } int main(void) { const Test x; cout << "[main] x.a= " << x.a << endl; //x.a = 20; エラーとなります。 //x.show(); 呼び出せません。 Test y; cout << "[main] y.a= " << y.a << endl; y.a += 12; //constオブジェクトでないので大丈夫 y.show(); cout << "値を変更しました。y.a = " << y.a << endl; return 0; }

オブジェクトxはconst宣言をしているのでデータメンバa に、値を代入することはできません。また、show関数を呼び出すこともできません。

一方、オブジェクトbは、const宣言をしていませんからデータオブジェクトに代入したり、show関数を呼び出したりできます。

当たり前ですがconst宣言をしていてもデストラクタとか、コンストラクタは自動的に呼ばれます。

いくらconstオブジェクトでも、値を書き換えないようなメンバ関数まで呼び出せないのはつらい?ですね。そこで、メンバ関数の宣言時に関数の最後にconstと付け足してやります。こうすれば呼び出すことができます。また、このconstは関数定義の所でも必ず記述しなくてはいけません。ではサンプルプログラムを見てみましょう。

#include class ConstTest { public: int a; ConstTest(); void show() const; //constの位置に注意 }; ConstTest::ConstTest(void) { a = 100; } void ConstTest::show(void) const //ここにもconst必要! { cout << "show関数が呼ばれました" << endl; cout << "a = " << a << endl; return; } int main(void) { const ConstTest x; x.show(); return 0; }

はい、左のようにshow関数を呼び出すことができました。では、このshow関数中にこっそり値を書き換えるような記述をしたらどうでしょうか?
何で、そんな変なことをするんだ!?
はい。ばかばかしいことでも実際に実験してみることが大事です。

error C2166: const型で宣言された項目を修飾しようとしました。

筆者の処理系では上のようなエラーメッセージが出てコンパイルできません。みなさんの処理系ではどうですか?
posted by シンビアン at 19:28| Comment(1) | TrackBack(0) | Symbian OS C++ 実践開発技法 | このブログの読者になる | 更新情報をチェックする

開発環境をそろえる

SDKの入手


FORUM NOKIAのDeveloper GuideからツールSDKダウンロードを選んでSeries 60 SDK (ソフト開発キット) for Symbian OS Nokia Edition (英語)をクリックします
ここで、2nd ed.FP 2,japanies(141MB)を選んでDownload nowを押すとForum Nokiaのアカウント(IDとパスワード)を聞いてきますのでID,パスワードを入れてください。
このアカウントはthe registration form から登録できます。これは、簡単なアンケートです。
アンケート後に送られて来たメールでレジストしてください。
日本語版と英語版のメールが来ますが、英語版が来るまでダウンロードは出来ません。
あとは、別ウインドウでダウンロードが始まりますので、終了するのを待ってください。(私の環境ではダウンロード後にIEが異常終了しましたが、ダウンロードはちゃんとできました。

Perlの入手


このSDKではPerlが必要ですので、ActivePerlをインストールしておきます

JRE1.4の入手


無くてもコンパイルは出来ますが、ツールが一部使えませんのでJava Run-Timeの1.4以降をインストールして置いてください。

SDKのインストール


画面の指示に従ってインストールしてください。最後に再起動があります

VC,BloandC++を使わないでコンパイル


実は、エミュレータを使わなければこのIDE環境は不要です。
いきなり実機でのテストになりますが

HelloworldBasicをコンパイルしてみる


* Windowsのコマンドラインを開きます
* HelloworldBasicのディレクトリーにいどうします
CD \Symbian\8.0a\S60_2nd_FP2_J\Series60Ex\helloworldbasic\group
* ビルドの準備を行います
bldmake bldfiles
* ビルドします
abld build THUMB UREL
* ビルドが終了すると¥SISフォルダにpkgファイルがファイルができますので、SISファイルに変換します
CD \Symbian\8.0a\S60_2nd_FP2_J\Series60Ex\helloworldbasic\sis
makesis helloworldbasic.pkg
* これでSISファイルができましたので、超勝手アプリのインストールと同じように702NKへインストールします。

※ちなみに\Symbian\8.0a\S60_2nd_FP2_J\epoc32\BUILDの下にMakeファイル、オブジェクトが作成されますので気をつけてください

AIFのアイコンを変更するときは一度、\epoc32\data\Z\system\APPS\アプリケーション名\の下のAIFファイルを消して、再度BUILDします。
これをしないとアイコンが変更されません

これで何とか、開発できる状態になりましたので、実際のプログラムの構成を見て行きたいと思います。
posted by シンビアン at 18:31| Comment(0) | TrackBack(0) | Symbian OS C++ 実践開発技法 | このブログの読者になる | 更新情報をチェックする

Helloworldbasocを解析

ディレクトリ


ファイル


意味
aif helloworldbasicaif.rss リソース定義ファイル
  qgn_menu_demo_cxt.bmp アイコン用BitMAP 44x44
  qgn_menu_demo_cxt_mask.bmp アイコン用BitMAP(MASK)
  qgn_menu_demo_lst.bmp アイコン用BitMAP 42x29
  qgn_menu_demo_lst_mask.bmp アイコン用BitMAP(MASK)
data helloworldbasic.loc ローカル定義(文字列)
  helloworldbasic.rss menu用リソース
  helloworldbasic_caption.rss CAPTION用リソース
group ABLD.BAT bldmake filesで作成されたバッチファイル
  bld.inf bldmake用定義ファイル
  helloworldbasic.mmp bld用プロジェクトファイル
inc helloworldbasic.hrh コマンドコード
  helloworldbasic.pan panic codes
  helloworldbasicapplication.h CHelloWorldBasicApplicationクラス(CAknApplication)
  helloworldbasicappui.h CHelloWorldBasicAppUiクラス(CAknAppUi)
  helloworldbasicappview.h CHelloWorldBasicAppViewクラス(CCoeControl)
  helloworldbasicdocument.h CHelloWorldBasicDocumentクラス(CAknDocument)
sis helloworldbasic.pkg SIS作成用ファイル(ABLDで生成)
  helloworldbasic.SIS makesisで作成
src helloworldbasic.cpp 本体(Applicationクラスを呼び出す)
  helloworldbasicapplication.cpp アプリケーションクラス(ドキュメントの作成とUIの呼び出し)
  helloworldbasicappui.cpp UIクラス(Viewクラスの呼び出しとコマンドの処理)
  helloworldbasicappview.cpp ウインドウの生成と画面の線画
  helloworldbasicdocument.cpp Docuemntクラス、この場合はドキュメントの処理は無し、UIの生成のみ

これで、大体の構成はわかったような気がします
posted by シンビアン at 18:26| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

コード変換

TBool CBTMSG60AppView::CharConvertL(TInt aTargetCode, TDes & aDes) const
{
TBuf unicodeMessage;
TBuf8 jiscodeMessage;

unicodeMessage.Copy(aDes);

CCnvCharacterSetConverter* converter = CCnvCharacterSetConverter::NewLC();
if(converter->PrepareToConvertToOrFromL(aTargetCode,
iEikonEnv->FsSession()) != CCnvCharacterSetConverter::EAvailable)
User::Leave(KErrNotSupported);

if(CCnvCharacterSetConverter::EErrorIllFormedInput == converter->ConvertFromUnicode(jiscodeMessage, unicodeMessage))
User::Leave(KErrArgument); //Leave if error in conversion.

aDes.Copy( jiscodeMessage );

return ETrue;

}



呼び出すときは

iAppView->CharConvertL(KCharacterSetIdentifierShiftJis, textData);

って感じで。他の漢字コードを使いたいときは一つ目の引数を変える。charconv.h charconv.lib も忘れずに!!
posted by シンビアン at 18:20| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

エディタの入力モードを変更したい

RESOURCE DIALOG r_dataquery_data_query
{
flags=EGeneralQueryFlags;
buttons = R_AVKON_SOFTKEYS_OK_CANCEL;
items =
{
DLG_LINE
{
type = EAknCtQuery;
id = EDataQueryDlgCIdDataQuery;
control = AVKON_DATA_QUERY
{
layout = EDataLayout;
control =
EDWIN
{
avkon_flags = EAknEditorFlagLatinInputModesOnly;
width = KMaxCommandNameLength;
lines = 1;
maxlength = KMaxCommandNameLength;
};
};
}
};
}
posted by シンビアン at 18:18| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

プログラミング

# サンプルプログラムのビルド(エミュレータ)

全てのプログラミングの習得についていえることですが、「習うより慣れろ」が大きいと思います。幸い、SDKにはたっぷりのサンプルが付いていますのでこれを活用します。自分の希望に近いサンプルを見つけたら、コマンドプロンプトを起動して下記のコマンドを実行してみましょう。

* cd "C:\Symbian\8.0a\S60_2nd_FP2_J_B\Series60Ex\APP_NAME\group"
* bldmake bldfiles
* abld build wins udeb

ここまでで、エミュレータで動作するビルドが出来ました。エミュレータを起動して動作を確認してみましょう。

# IDE ターゲット用のビルド

* abld makefile vc6

"C:\Symbian\8.0a\S60_2nd_FP2_J_B\epoc32\BUILD\SYMBIAN\8.0A\S60_2ND_FP2_J_B \SERIES60EX\APP_NAME\"以下(設定により若干変わることあり)に VC6 の設定ファイル (*.dsw) が出来ます。これをダブルクリックして VC6 の IDE 環境でデバッグすることが出来ます。
posted by シンビアン at 17:20| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

UID

UID とは Symbian OS 上で動作するアプリケーションを特定するための ID です。各アプリケーションが一意の ID 保持しています。自作アプリの外部公開を目指す人はこの UID を取得する必要があります。uid AT symbiandevnet.com にメールで問い合わせます。
posted by シンビアン at 17:19| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

エミュと実機のビルド

エミュと実機でビルドの条件を少しだけ変えたい場合は以下のようにする。(NextTrain60 から引用)

#if __WINS__
_LIT(KTbls, "C:\\nexttrain60\\tbls\\");
#else
_LIT(KTbls, "E:\\nexttrain60\\tbls\\");
#endif

エミュレータのときは "_LIT(KTbls, "C:\\nexttrain60\\tbls\\");" がコンパイルされる。
posted by シンビアン at 17:15| Comment(1) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

UIDの変更箇所

本格的なアプリケーション開発を行うようになると UID が必要になってくる。アプリケーションを開発するときはサンプルコードを改造したり、Application Wizard を用いて開発することが多いと思う。どっちにしても UID を後から設定する必要がある。(ちなみに筆者は Application Wizard でうまく設定出来たためしがない…。)以下のファイルを編集する

* 〜Application.h
* 〜Aif.rss
* 〜.mmp
* 〜.pkg
posted by シンビアン at 17:12| Comment(1) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

日本語リソースの設定

Series60青本の230ページあたりに詳しい記述があるが、日本語(2バイト?)を取り扱うときは以下の点にも注意する必要がある。

* *.loc ファイル中に CHARACTER_SET UTF8 を記述する。*.loc ファイルそのものも UTF-8 形式にする
* 各言語のリソースを記述する *.l01 ファイルなども UTF-8 形式にする

ちなみに日本語の場合だと *.l32 になる。

*.pkg ファイルの記述を工夫すると各言語セットごとにファイル構成を変えることが出来る。これにより、各国に合わせたビットマップや音声などをセットアップすることが出来る。
posted by シンビアン at 17:11| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

ファイルの読み込み

# BOM の取り扱い

日本語を取り扱うときは UTF-8 形式のファイルを取り扱う必要がある。UTF-8 形式のファイルはファイルの先頭にBOMが付加されているのでファイルを読み込ませるときは予め、この部分を飛ばしてから読み込ませる必要がある。
# UTF-8 -> UNICODE

内部的には UNICODE で処理されているので UTF-8 を UNICODE に変換する必要がある。具体的には以下のAPIを使って変換する。

const TInt returnValue=CnvUtfConverter::
ConvertToUnicodeFromUtf8(unicodeOutputBuf, utf8Buf);
posted by シンビアン at 17:10| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

Designing C++ Applications for Series 60

用語集
このドキュメントでは、以下の用語および略語が使用されています。
用語 説明
Avkon Series 60 エクステンションおよび Uikon とその他 Symbian OS アプリケーションフレームワークへの変更
Uikon すべての Symbian OS デバイスに共通の UI およびコントロールのフレームワーク
posted by シンビアン at 17:05| Comment(1) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

Resource Struct RSS_SIGNATURE

RSS_SIGNATURE
Support

Supported from 6.0
Description

A utility struct that, used as the first resource in a resource file, provides a way to record what the resource offset is.

It contains two 32-bit fields: a version number and a self-referencing link. The RResourceFile::ConfirmSignatureL() function reads the signature resource and initialises the offset value from this resource.
See also:

* RResourceFile::ConfirmSignatureL()

LONG signature=EEikResourceSignatureValue;

A resource file version number.
SRLINK self;

A link to the current resource (i.e. itself). This should not be assigned a value.
posted by シンビアン at 17:01| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

Series 60 アプリケーションフレームワーク

オプションメニューの使用
4.1 オプションメニュー
Avkon オプションメニューは、 .rss ファイルに指定されているメニューバーとメニューペインから構築されます。メニューは、 EAknSoftkeyOptions ID によりバインドされている場合 WINS エミュレータの 'F1' キーまたは 'Options' ソフトキーの操作に応じて開きます。既存の 'Options' ソフトキーペインを使おうとするアプリケーションビューは、 R_AVKON_SOFTKEYS_OPTIONS_BACK CBA リソースを使用するべきです。
4.2 メニューセクションの定義
メニューペインセクションは MENU_PANE リソースストラクチャとして定義されます。メニューペインはそれぞれのメニューセクションに定義されるべきです。ひとつのセクションはそれぞれのメニューペイン領域:システム、アプリケーション、ビュー、コンテキストに使用されます。

メニューペインは次のように定義されます。

RESOURCE MENU_PANE r_system_menu
{
items =
{
MENU_ITEM { command = ECmdCut; txt = "Cut"; },
MENU_ITEM { command = ECmdCopy; txt = "Copy"; },
MENU_ITEM { command = ECmdPaste; txt = "Paste"; }
};
}

サブメニューペインは、サブメニューペインのリソース名を使って cascade パラメータにより指定できます。

例:

MENU_ITEM { command = ESystemOptions; txt =
"System Options"; cascade = r_system_options_menu; }

4.3 メニューセクションの組合せ
メニューセクションは MENU_BAR リソースにより結合されます。 これはメニューを構成するために結合されるすべてのセクションを表示します。メニューペインは、最後のセクションから先頭のセクションの順で定義されます。

メニューバーは次の様に定義されます。

RESOURCE MENU_BAR r_menuapp_menu
{
titles =
{
MENU_TITLE { txt = "System"; menu_pane =
r_system_menu; },
MENU_TITLE { txt = "App"; menu_pane =
r_app_menu; },
MENU_TITLE { txt = "View"; menu_pane =
r_view1_options_menu; },
MENU_TITLE { txt = "Context"; menu_pane =
r_context1_menu; }
};
}

オプションの txt パラメータは、明確にすることのみを目的としており、画面上ではまったく表示されません。但し、これらパラメータはリソースファイルには保存されます。

デフォルトメニューバーは EIK_APP_INFO リソースにより参照されます。これはアプリケーションの起動時に提供されるメニューバーです。

ビューアーキテクチャが使用される場合は、ビューのリソースストラクチャ内に各ビューのメニューリソースを設定するオプションがあります。
4.4 メニューセクションの変更
アプリケーションが使用するメニューバーリソースを変更することによりアプリケーション内でいつでもメニューセクションを変更することができます。

iEikonEnv->AppUiFactory()->MenuBar()->
SetMenuTitleResourceId(MENU_BAR_RESOURCE_ID);

これは、セクションの中のどれかひとつの内容を変更する必要があるたびに実行されるべきです。従って、考えられるメニューセクションの組合せの数だけ MENU_BAR リソースが定義されているべきです。つまり、ひとつのメニューバーは、ビューとコンテキストオプションの各組み合わせごとに定義されるべきだからです。

なお、ビューアーキテクチャが使用されていて、そのビュー自体のメニューシステムが現在使用されている場合、それは変更が必要なメニューバーの内容となります。

iMyView->MenuBar()->SetMenuTitleResourceId
(MENU_BAR_RESOURCE_ID);

4.5 メニューアイテムの変更
個々のメニューアイテムは、メニューが表示されるたびに変更される可能性があります。これにより、アプリケーションはアプリケーションの状態に応じてメニューアイテムを追加したり、削除したりできます。

アプリケーションUIは下記の仮想関数をオーバーライドするべきです。

void DynInitMenuPaneL
(TInt aResourceId, CEikMenuPane* aMenuPane);

これは、セクションのそれぞれがメニューに追加された後にコールされます。そして、追加されたセクションとビルドされているメニューペインのリソース ID でコールされます。

アプリケーションUIは下記の仮想関数もオーバーライドできます。

void DynInitMenuBarL
(TInt aResourceId, CEikMenuBar* aMenuBar);

これは、どんなセクションがメニューに追加される場合でもその前にコールされます。これはメニューに追加されるセクションを動的に変更するのに使用できます。つまり、コンテキストメニューセクションのリソース ID をあるアプリケーションの状態のために変更するために使用できます。
posted by シンビアン at 13:51| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

Series 60 アプリケーションフレームワーク

ベースクラス
3.2.1 CAknDocument

このクラスはアプリケーションドキュメントのベースクラスとして提供されます。このクラスを使うことにより、アプリケーションドキュメントファイルへのアクセスは開始されません。これは、大多数の Avkon アプリケーションでは許容されます。
3.2.2 CAknAppUi

すべての Avkon アプリケーション( Eiksrv とは別)はこのクラスから派生しなければなりません。

このクラスはいくつかの Avkon 固有の機能をサポートしています。これら機能については、 SDK 説明書で詳しく解説されています。

* KeySound サポート
* CBA ペインと ステータスペインのアクセサリー
* TextResolver - CAknAppUi::HandleError() から報告される Avkon 特有のエラー
* Avkon ビューアーキテクチャインテグレーション
* コントロールダンピング- デバッグ機能

3.2.3 CAknViewAppUi

すべてのビューアーキテクチャに基づくアプリケーションは、このクラスから派生しなければなりません。詳細は SDK 説明書に記載されています。
posted by シンビアン at 13:48| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

Series 60 アプリケーションフレームワーク

CEikApplication から派生する CAknApplication クラスが提供されます。このクラスは再実装により CEikApplication を特化させます。

* PreDocConstructL()
* OpenIniFileLC(RFs& aFs)

PreDocConstructL はまず構築中のアプリケーションのインスタンスが既に存在していないかどうかチェックするために実装されます。もしもそのインスタンスが既に存在する場合は、アプリケーションは存在するインスタンスに切り替え、その後終了します。このチェックは非埋め込みアプリケーションに対して行われます。

デフォルトでは、 Series 60 アプリケーションは、 .ini ファイルをサポートしていません。 OpenIniFileLC は、コールされるとただリーブする単純な実装によりオーバーライドされます。 .ini ファイルが使用される場合、アプリケーションはそのアプリケーションクラス内でこのメソッドを実行することを強制され、 CEikApplication::OpenIniFileLC をコールします。
posted by シンビアン at 13:45| Comment(0) | TrackBack(0) | Series60プログラミングテクニック | このブログの読者になる | 更新情報をチェックする

ネイティブアプリケーションが携帯電話を変える! いよいよ日本でも本格デビュー

Symbian OSは、音声通話以外にさまざまなデータ処理機能を持つハイエンド携帯電話(=スマートフォン)用の標準OSとして幅広く採用されていますが、そこで使用されるアプリケーションは、一般のPCソフトのようにインストールして利用できるOS固有のアプリケーション(ネイティブアプリケーション)の時代を迎えて、大きく変化しようとしています。本書のテーマであるSymbian OS+ミドルウェア「Nokia Series 60」の環境は、その代表的な開発プラットフォームです。ヨーロッパをはじめとして海外ではすでにこの環境を実装したスマートフォンが市場を牽引していますが、日本でもノキア・ジャパンより端末の発売が発表されるなど、業界や開発者の注目を集めています。
posted by シンビアン at 10:39| Comment(0) | TrackBack(0) | Forum Nokia | このブログの読者になる | 更新情報をチェックする

ルネサス テクノロジ、Symbian のユーザー支援プログラムに参加

株式会社ルネサス テクノロジと英国 Symbian は2005年1月19日、ルネサステクノロジの super AND 型フラッシュメモリのドライバソフトウェアが、Symbian のユーザー支援プログラム「Companion Technology Program(CTP)」に参加し、Symbian OS を公式にサポートする、と発表した。

これによりルネサス テクノロジは、Symbian OS のライセンシーとして Symbian OS を搭載したスマートフォンを開発・製造する携帯電話メーカーに対し、同ドライバソフトウェアの検証時間を短縮し、動作確認できるシステムを提供する。

super AND 型フラッシュメモリは、データの読み出しを行う際、メモリチップ自身が変化した箇所を検出し訂正する「エラー訂正機能」や、書き換え回数を平準化する「ウェアレベリング機能」など、従来システム側で対応していたメモリ管理機能を内蔵し、システム設計負担を軽減させる。

CTP は、富士通、Motorola、Nokia、シャープ、Sony Ericsson など Symbian OS のライセンシーの技術革新を促進することを目指しており、Symbian OS ライセンシーに対して、事前に Symbian OS 環境での統合や動作確認された技術を提供することで、開発期間の短縮と、技術的なリスクを抑え製品の完成度を高めることができる。

また、Symbian OS ライセンシーは、CTP のソリューションを採用する際、各ソリューション・ベンダーと直接ライセンス交渉することも可能だ。
posted by シンビアン at 10:08| Comment(0) | TrackBack(0) | Forum Nokia | このブログの読者になる | 更新情報をチェックする

リファレンスの初期化

ここで、リファレンスの初期化についてもう少し詳しく見ていくことにしましょう。

まず、const でない T 型のリファレンスを初期化する場合ですが、この場合の右辺には以下のように T 型の左辺値だけが許されます。

001 int test();
002
003 void f()
004 {
005 int i;
006 unsigned int ui;
007 long l;
008
009 int& r1 = i; // OK. i は int 型の左辺値。
010 int& r2 = r1; // これも OK.
011 int& r3 = ui; // error. unsigned int は int じゃない。
012 int& r4 = l; // error. long も int じゃない。
013 int& r5 = test(); // error. 一時変数は左辺値じゃない。
014 int& r6 = 3; // error. 定数は左辺値じゃない。
015 }

ちなみに、左辺値かどうかは、値を代入することができるかどうかで判断します。13,14行目のような一時変数や定数は値を代入することができませんので、それらは左辺値でないことが分かります。

const でない T 型のリファレンスを初期化できるのは、T 型の変数か const でない T 型のリファレンスだけでした。それに対し、const T& は様々な値で初期化することができます。T 型に変換することができれば T 型でなくてもかまいませんし、一時変数や定数でもかまいません。

001 int test();
002
003 void f()
004 {
005 int i;
006 unsigned int ui;
007 long l;
008
009 const int& r1 = i; // OK.
010 const int& r2 = r1; // OK.
011 const int& r3 = ui; // OK.
012 const int& r4 = l; // OK.
013 const int& r5 = test(); // OK.
014 const int& r6 = 3; // OK.
015 }

また、13行目の r5 にはテンポラリオブジェクトの寿命に関する興味深い点があります。test() の戻り値のようなテンポラリオブジェクトの寿命は、式の終わり(つまり13行目のセミコロン)までとなっているはずです。ところが r5 は14行目以降でも問題なく使うことができます。つまり、テンポラリオブジェクトを const なリファレンスで参照することによって、そのテンポラリオブジェクトの寿命を延ばすことができるのです。この点に関しては、「const なリファレンスの初期化によるテンポラリオブジェクトの延命」にて詳しく説明したいと思います。

最後に、const なリファレンスを初期化できない例として、volatile の使用を挙げておきます。

001 void f()
002 {
003 volatile int vi = 1;
004 const volatile int cvi = 2;
005
006 const int& r1 = vi; // error. volatile な変数では初期化できない。
007 const int& r2 = cvi; // error. volatile は const であっても信用できない。
008 const volatile int& r3 = vi; // OK.
009 const volatile int& r4 = cvi; // OK.
010 }

volatile は「移り気な・気まぐれな」という意味のとおり、文脈とは関係のないところで変化するかも知れない変数を意味します。よく、最適化の対象にならない変数という解説を見かけますが、勝手に変化するかもしれないから結果的に最適化できないだけのことです。どちらにしろ volatile を使うことは稀かと思いますが、もし使う場合にはきちんと volatile をつけて参照するようにしましょう。
posted by シンビアン at 09:13| Comment(0) | TrackBack(0) | Symbian OS C++ 実践開発技法 | このブログの読者になる | 更新情報をチェックする

関数とリファレンス

関数には引数と戻り値が存在し、それぞれに変数, ポインタ, リファレンスを用いることができます。また、関数へのポインタと同様に、関数へのリファレンスというものも存在します。本章では、このような関数とリファレンスの関係について考えていこうと思います。

001 void f1(int i)
002 {
003 i += 10; // 単なるコピーに10を加える。
004 }
005
006 void f2(int* p)
007 {
008 *p += 10; // ポインタの参照先に10を加える。
009 }
010
011 void f() {
012 int i = 10;
013
014 f1(i);
015 cout << i << endl; // i の値は変化しない。(10を出力)
016 f2(&i);
017 cout << i << endl; // i の値は変化する。(20を出力)
018 }

しかし、C++ にはリファレンスによる「参照渡し」が存在します。仮引数にリファレンスを指定すれば、関数呼び出しによって引数を変更することができます。

001 void f3(int& i)
002 {
003 i += 10; // i は与えられた変数の別名。
004 }
005
006 void f() {
007 int i = 10;
008
009 f3(i);
010 cout << i << endl; // i の値は変化する。(20を出力)
011 }

つまり、C++ には関数呼び出しによって引数を変更する手段が2通り存在することになります。ポインタとリファレンスで述べたようにポインタには何も参照しない状態(ヌルポインタ)が存在するため、ポインタを用いて引数を変更する場合にはヌルポインタに気をつける必要があります。それでは、ポインタとリファレンスの使い分けは、関数呼び出しによって引数を変更する場合でも「何も参照しない場合があるかどうか」で区別すればよいのでしょうか。これに関しては「プログラミング言語 C++ 第3版」の「5.5 リファレンス」にて以下のように述べられています。

しかし、プログラムを読みやすくするために、引数を変更する関数は避けた方がよい場合が多い。代わりに、明示的に値を返すか、ポインタ引数を要求するのである。
- 中略 -
increment(x) という表記は、x=next(x)、incr(&x) とは異なり、xの値が変更されようとしていることを示す手掛かりを読者に与えない。そのため、“ただの”リファレンス引数は、リファレンス引数が変更されるというヒントをはっきりと与えるような名前が付けられた関数だけで使うようにすべきである。

先ほどの例で言えば、「f1(i) と同じような感覚で f3(i) を呼び出して i の値が変化してしまうと読みづらいプログラムになってしまう」といったところでしょうか。f2(&i) であれば、i の値が変更されるかも知れないという事を意識して以降の処理を読み進めることができます。

ところで、C言語の時代では、引数を変更する以外にもポインタ引数を用いる場合がありました。それは、コピーに時間がかかる場合です。先ほども述べたように「値渡し」では引数がまるごとコピーされてしまいます。これでは巨大な構造体を引数として渡したい場合に無駄なコピーが大量に発生してしまうため、C言語ではポインタを渡してこの問題に対処していました。このままでは引数を変更することもできますが、もし、引数を変更する必要がない場合には const なポインタを渡すようにするだけです。

そして、同じ事がリファレンスにも当てはまります。C++ では、無駄なコピーを抑えるためにリファレンスを用いることができ、値を変更したくない(させない)場合には const なリファレンスを使用します。この const なリファレンス引数は、C++ のプログラムにおいて最も頻繁に登場するリファレンスといえるでしょう。

引数の「参照渡し」には他にも以下のような利点があります。

* 不要なコンストラクタの呼び出しを抑える。
* スライシングを防止する。
* コピーコンストラクタが public でなくても使える。

詳しくは「Effective C++ 改訂2版」の「22項 値渡しよりも、リファレンス渡しを使おう」を参照してください。
posted by シンビアン at 09:02| Comment(0) | TrackBack(0) | Symbian OS C++ 実践開発技法 | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は1年以上新しい記事の投稿がないブログに表示されております。