ATLAS-Japan C++ Tutorial in 2016

Introduction

ここでは教科書に書いてあるような基本的なことはスキップします。
世の中にはたくさんの教科書がありますし、今の時代ググれば基本情報は出てくるので、以下は特に知っておいた方が良いことを書きます。

したがってそこで不安になる方は

あたりで勉強することをお勧めします。

クラスとオブジェクト指向

C++ではクラスというものが使えるようになっています。これを有用に使うにはオブジェクト指向という概念を知っておく必要があります。
HELPまったくクラスを知らない人向けですが、クラスとはそれ自身が属性(メンバー変数)と操作(メンバー関数)を持った変数の型のようなものだと思って下さい。

さて、プログラミングをすると君たちはその世界の中で神様になれます。
プロセス内に新しいものを創造でき、それらを大量に発生させることが可能になります。

具体的にどうやってクラスを定義するか等は 最後のExerciseと黒板を使って触れることにしますが、
一度クラスを定義してしまえば(されていれば)、基本的に変数や構造体と使い方はあまり変わりません。
なのでオブジェクトを作成する基本は、これまで

int var;
とやっていたように
MyClass obj;
みたいな感じになります。ポインタで作成することも可能で、詳しくは後で述べます。

クラスのメンバーには public, private, protected という属性を付けることが可能です。
むしろ明示することが推奨されています。 objectから指定できるものが public, そうでないものが private (or protected)です。
public メンバーにはメンバアクセス演算子 (. もしくは ->)を使ってアクセスできます。

MyClass obj;
obj.Set("hoge");

MyClass* obj = new MyClass();
obj->Set("hoge2");

オブジェクト指向はカプセル化が大事で、一般的に外からメンバー変数を変更することを 良しとしません。
なので、それらにアクセスするメンバー関数public, メンバー変数private を使います。
protected 属性は自分のクラスの中、もしくは継承先(後述)のクラスでアクセスできるメンバーを明示したものです。

その他知っておくとためになる知識

オブジェクトを作る(消す) - memory allocation, ポインタ, 参照

HELP Will Buttinger - offline software tutorial 2015のスライドより拝借しています
オブジェクトの作成・削除・memory allocation
オブジェクトをC++で作ると、それ用のメモリーがあてがわれます。 これには2種類 (stackheap)があります。
MyClass obj;   // creating instance of MyClass on the stack
int myInt;       // creating instance of int on the stack
MyClass* obj2 = new MyClass();    // creating instance of MyClass on the heap
int* myInt2 = new int();              // creating instance of int on the heap 

stackは

  • read/writeが速い
  • heapよりはサイズが小さい
  • 自動的に削除される(自分で delete する必要がない)
一方でheapは
  • stackよりは遅い
  • stackよりはサイズが大きい
  • 自分で delete などをしないといけない(ROOT上では多少やってくれますが)
一つ例を上げると
int main()
{
  int a;
  int* b = new int;     // note: no brackets means using default constructor
  {
    int c;
    int* d = new int;
  }      // At this point c is deleted. The int that d points to hasn't though... MEMORY LEAK!!
  delete b;      // good, we're cleaning up our heap allocation
}     // a gets deleted from the stack here.
ポインタとは一言で言えば、メモリ上のインスタンスのアドレスです。
MyClass obj;    // creating instance of MyClass on the stack
int myInt;        // creating instance of int on the stack
MyClass* obj2 = new MyClass();     // creating instance of MyClass on the heap
int* myInt2 = new int();              // creating instance of int on the heap

MyClass* obj3 = &obj;     // obj3 points at obj
int* myInt3 = &myInt;     // myInt3 points at myInt
アドレスってのはメモリではとても小さいです (64bitマシンで8 bytes (=64 bits))
なので関数に受け渡すときなどはオブジェクト自体を渡すよりもポインタを渡したほうが早くなります。
  • basicなtype (bool, int, float, ...)では差がありませんが。
  • 関数の引数でインスタンスを与えると同じ内容のオブジェクトがstack上にコピーされます。
    そしてその関数が終わるときに自動的に消されます。(i.e. オリジナルのオブジェクトにはアクセスしていません)
  • 引数にポインタを与えるとアドレスのコピーが作られます。
    コピーされたアドレスにアクセスすることになるのでオリジナルのオブジェクトにアクセスすることになります。
void myBadFunction(MyClass obj) { obj.myMethod(); }
void myGoodFunction(MyClass* obj) { obj->myMethod(); }

int main()
{
  MyClass obj;
  myGoodFunction(&obj);
}

なんかしらオブジェクトを作ったら、必ず「 いつ 」「 どこで 」消える(消す)のかを考える癖を付けましょう。
stackの場合、自動で削除されます。でもscope (中括弧 { }) の外側ではアクセスしてはいけません。

MyClass* b;     // created a pointer, but not made it point at anything
{
    MyClass c;
    b = &c;    // b now points at c.
}    // c is deleted, but we've dangerously kept it's address (held in b)
b->myMethod();    // Can't do this, c doesn't live at the address held by b anymore
heapの場合、必ず自分でdeleteをしないといけません。
ダメな例を示すと
void myBadFunction() {
     MyClass* obj = new MyClass();
     if(obj->someMethod()) return;
     delete obj;     // too late if someMethod() returned true...
}

参照
最後に参照について復習しましょう。
  • MyClassタイプのインスタンスのポインタの型は MyClass* です。
  • MyClassタイプのインスタンスの参照の型は MyClass& です。
  • 参照はアドレスの代わりにオブジェクトを指すことができます。
MyClass obj
MyClass* objPtr = &obj;
MyClass& objRef = obj;

継承, Virtual 関数, キャスト

継承と virtual 関数
継承の身近な例を示すと
TH1D.png
実際にクラスリファレンスを見てみましょう。
例えば Add(const TH1* h1, ...) という関数はTH1Dには定義されていません。 TH1クラスで定義されています。これはTH1DやTH1Fで同じ機能なので親クラスで定義されています。
なのでオブジェクト指向プログラミングをする上で、クラスリファレンスを見ることは大変重要です。
親クラスに何の操作があるか、もしくは例えばGet...()で返してくるオブジェクトで何が出来るか、が大変重要であり、要領を掴めればこれで(基本的に)何でも出来るようになります。

メンバー関数を一つ持っているクラスと、それを継承したクラスを作ってみます。

class MyClassA
{
 public:
  void myMethod() { std::cout << "It's MyClassA" << std::endl; }
};

class MyClassB : public MyClassA
{
 public:
  void myMethod() { std::cout << "It's MyClassB" << std::endl; }
};
これを実際に使用してみるとポインタ / 参照した型のタイプに依存した結果が示されます。
MyClassB* b = new MyClassB();
MyClassA* b_as_a = b;

b->myMethod();        // prints "It's MyClassB"
b_as_a->myMethod();   // prints "It's MyClassA"
ここで関数をvirtualにしてみましょう。
class MyClassA
{
 public:
  virtual void myMethod() { std::cout << "It's MyClassA" << std::endl; }
};

class MyClassB : public MyClassA
{
 public:
  virtual void myMethod() { std::cout << "It's MyClassB" << std::endl; }
};
するとコンパイラが呼ぶ関数は実際のインスタンスのものになります。
MyClassB* b = new MyClassB();
MyClassA* b_as_a = b;

b->myMethod();        // prints "It's MyClassB"
b_as_a->myMethod();   // prints "It's MyClassB"
このようなことをポリモーフィズム(多様性)と言います。
Casting
キャストについても学んでおきましょう。
  • C-style cast
  • static_cast
  • dynamic_cast
  • reinterpret_cast
  • const_cast
の5つありますが知っておいてほしいものはstatic_castとdynamic_castだけです。それ以外は極力使わないようにしましょう。細かいことは言いませんが危険をはらんでいるんです。

dynamic_cast

安全な基底クラスから派生クラスへのキャスト(ダウンキャスト) のために使います。
(派生クラスから基底クラスへのキャスト(アップキャスト)も特に問題なくできるが、そちらはstatic_cast推奨)

static_cast

主に派生クラスから基底クラスへのキャスト(アップキャスト)用。皆さんがやりたい(と思われる)C-styleキャストはこれに置き換えましょう。
危険なキャストをコンパイルエラーではじく事は可能です。

使い方一つだけ例を示すと

int ival;
long lval = static_cast<long>(ival);

Coding rule

今日言いたいことは実はここだけであったりするくらい重要なお話です。とにかく
  • 自分が読んですぐに理解できるコードを書く!
  • 他の人も使う(使って欲しい)ならば、シェアしうるコードを書く!
とにかく一定のルールに従ったコードを書くことが重要です。(例えばメンバー変数なら m_ で始めるとか)

ATLASでは推奨コーディング法というものがあって、ここに最新のがあります。

一部古い情報もありますが日本語版も坂本先生が用意してくれています。

あと、一つのソースコードのファイルが1000行とか超えるようならそれはデザインが間違えてると思いましょう。
きちんとしたオブジェクト志向にのっとって作っておけば(コードの数は多少増えても)そういうことは起こらないはずです。
(1万行を超えるコードなんて読む気も失せるでしょ?)

Exercise 1 - C++ programming

自分で一つクラスを用いたプログラムを作ってみましょう。
ここでは

  • C++コードの基本的なコンパイル方法を知る
  • 一つのプログラムのために複数のコードを用いる(ヘッダファイルを作る)
  • クラスを作ってみる
にフォーカスします。

とりあえずどこか作業する場所を作ります。

[junpei@login01 ~]$ mkdir cxxtut2015
[junpei@login01 ~]$ cd cxxtut2015

ではParticleという粒子のクラスを作ります。 このクラスは

  • 粒子の名前
  • エネルギー
  • 電荷
の3つのパラメータを保持するとします。 また粒子の情報を出力できるという機能を持つことにします。
クラス定義はヘッダファイル (Particle.h) を書いていきましょう。
#ifndef Particle_h
#define Particle_h
 
#include <string>
 
class Particle
{
 public:
  Particle();
  virtual ~Particle();
 
  virtual void printInfo();

  inline void setName(std::string name) { m_name = name; }
  inline void setEnergy(double energy) { m_energy = energy; }
  inline void setCharge(const int charge) { m_charge = charge; }
 
  inline std::string getName() const { return m_name; }
  inline double getEnergy() const { return m_energy; }
  inline int getCharge() const { return m_charge; }
  
 protected:
  std::string m_name;            ///< particle name
  double m_energy;             ///< 4-momentum
  int m_charge;                        ///< charge
};
 
#endif

Particleクラスの関数の中身は(Particle.cxx)に実装しましょう。

#include "Particle.h"

#include <iostream>

Particle::Particle()
{
  m_name = "NOT DEFINED";
  m_energy = 0.0;
  m_charge = 0;
}
 
Particle::~Particle()
{}

void Particle::printInfo()
{
  std::cout << "Particle Name = " << m_name
                 << ", energy = " << m_energy
                 << ", charge = " << m_charge << std::endl;
}

そしてmain()を作ってクラスを呼びます (exercise.cxx)

#include "Particle.h"

int main(void)
{
  Particle electron;

  electron.setName("electron");
  electron.setEnergy(1000.);   // 1000 MeV
  electron.setCharge(-1);

  electron.printInfo();

  return 0;
}

ではコンパイルしてみます。

[junpei@login01 cxxtut2015]$ g++ -c Particle.cxx
[junpei@login01 cxxtut2015]$ g++ exercise.cxx Particle.o -o exercise.exe

せっかくなので実行
[junpei@login01 cxxtut2015]$ ./exercise.exe

Edit | Attach | Watch | Print version | History: r7 < r6 < r5 < r4 < r3 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r7 - 2016-12-25 - JumpeiMaeda
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    Main All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright & 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback