【C++】ライブラリとリンク
ライブラリ
静的リンク
これに対応したライブラリが静的リンクライブラリ
動的リンク
これに対応したライブラリが動的リンクライブラリ(共有ライブラリ)
静的リンクと動的リンクの違いの確認
静的リンクライブラリの作成
MyStatic.h
#pragma one
class MyStatic
{
public:
int Calc(int a, int b);
}
MyStatic.cpp
#include "MyStatic.h"
int MyStatic::Calc(int a, intb)
{
return a + b;
}
動的リンクライブラリの作成
MyDynamic.h
#pragma one
#ifdef MYDYNAMICLIB_EXPORTS
#define MYDYNAMICLIB_API __declspec(dllexport)
#else
#define MYDYNAMICLIB_API __declspec(dllimport)
#endif
class MyDynamic
{
public:
MYDYNAMICLIB_API int Calc(int a, int b);
}
MyDynamic.cpp
#include "MyDynamic.h"
int MyDynamic::Calc(int a, int b)
{
return a + b;
}
実行ファイルの作成
#include <iostream>
#include <MyDynamic.h>
#include <MyStatic.h>
int main()
{
auto myDynamic = std::make_unique<MyDynamic>();
auto myDynamicRet = myDynamic->Calc(5, 10);
std::cout << myDynamicRet <<std::endl;
auto myStatic = std::make_unique<MyStatic>();
auto myStaticcRet = myStatic->Calc(1, 20);
std::cout << myStaticRet <<std::endl;
}
実行①
実行②
【C++】リンカー
リンカー
宣言(declaration)
以下のような関数を宣言すると、コンパイラにMyPrint(symbol)という関数が存在する(function exists)ということを伝える。
void MyPrint(std::string const& msg);
定義(definition)
void MyPrint(std::string const& msg)
{
std::cout << msg << std::endl;
}
リンカーの挙動1
MyPrintの定義が存在しないが、MyPrintを使用している箇所がないためリンカーが探しにいくことはない。
#include <iostream>
void MyPrint(std::string const& msg);
int Addition(int a, int b) {
// MyPrint("Use Addition");
return a + b;
};
int main() {
auto t = Addition(1, 2);
}
リンカーの挙動2
一見すると、リンカーの挙動1と同様に、コンパイルもビルドも成功するように思えるが、ビルド時にリンキングエラーが発生する。
これは、ファイル内でAdditionが使用されていないということは、他のファイルで使用される可能性があるため、リンカーがMyPrintを探すため。
#include <iostream>
void MyPrint(std::string const& msg);
int Addition(int a, int b) {
MyPrint("Use Addition");
return a + b;
};
int main() {
//auto t = Addition(1, 2);
}
【C++】実行までの流れ
実行ファイルの作成
テキストファイル
Javaとは異なり、クラス名とファイル名が違っても良い。
プリプロセス
代表的に#includeディレクティブがあり、プリプロセッサが#includeで指定したファイルをコピー&ペーストしてくれる。
極端な例ではあるが、以下の場合、プリプロセッサが※の箇所にPreprocess.hをペーストしてくれる。
Preprocess.h
}
Main.cpp
#include <iostream>
int main(){
std::cout << "Test" << std::endl;
#include "Preprocess.h" // ※
プリプロセスの結果のファイルは*.iファイルで、プロジェクトのプロパティ>[構成プロパティ]>[C/C++]>[プリプロセッサ]>[ファイルの前処理]を"はい(/P)"に指定すると出力される。コンパイル
コンパイル時にエラー(コンパイルエラー)が発生した場合のエラーメッセージはC~
コンパイルされた結果は*.objファイルとして出力される。
ファイルを保存後"Ctrl+F7"でコンパイルすることが可能。
バイナリ形式の一歩手前のアセンブリファイルを出力するためには、プロジェクトのプロパティ>[構成プロパティ]>[C/C++]>[出力ファイル]>[アセンブリの出力]を"アセンブリコードのみ(/FA)"に指定
リンキング
コンパイルに記載の通り、C++は分割コンパイルであり、コンパイル結果の各オブジェクトファイルは他のオブジェクトのことが分からない。
そこで、リンカがグローバル変数や関数の呼び出し参照を結合する。
リンキングが失敗した際のエラー(リンキングエラー)が発生した場合のエラーメッセージはLNK~
エントリーポイント(main関数)
特殊な関数で、戻り値がint型であるが、returnしなくても良い。
【C#】Enumerable.OfType<TResult>(IEnumerable) メソッド
OfType<TResult>
public static IEnumerable<TResult> OfType<TResult>(this IEnumerable source)
{
if (source == null) throw Error.ArgumentNumm("source");
return OfTypeIterator<TResult>(source);
}
static IEnumerable<TResult> OfTypeIterator<TResult>(IEnumerable source)
{
foreach (object obj in source)
{
if (obj is TResult) yield return (TResult)obj;
}
}
自作クラスで確認
internal abstract class MyElement : IEnumerable<MyElement>, IEnumerable
{
internal List<MyElement> children;
internal MyElement()
{
this.children = new List<MyElement>();
}
public IEnumerator<MyElement> GetEnumerator()
{
foreach (var child in children) yield return child;
}
IEnumerator IEnumerator.GetEnumerator()
{
return ((IEnumerable)children).GetEnumerator();
}
}
internal class MyTableRow : MyElement
{
internal void AddChild(MyElement myElement) this.children.Add(myElement);
}
internal class MyTableCell : MyElement
{
}
internal class MyTableRowProperty : MyElement
{
}
internal class Dummy : MyElement
{
}
static void Main()
{
var tr = new MyTableRow();
tr.AddChild(new MyTableCell());
tr.AddChild(new MyTableRowProperty());
tr.AddChild(new MyTableCell());
tr.AddChild(new MyTableCell());
tr.AddChild(new MyTableRowProperty());
var tcs = tr.OfType<MyTableCell>();
System.Console.WriteLine(tcs.Count()); // 3
var trPrs = tr.OfType<MyTableRowProperty>();
System.Console.WriteLine(trPrs.Count()); // 2
var dummys = tr.OfType<Dummy>();]
System.Console.WriteLine(dummys.Count()); // 0
}
【C#】IEnumerable<T>インタフェースの実装
IEnumerable<T>インタフェースの実装
に記載の通り、IEnumerable<T>インタフェースは抽象メソッドGetEnumerator()を持っているため、IEnumerable<T>インタフェースを実装する際には、GetEnumerator()を実装する必要がある。
また、IEnumerator<T>を継承するとIEnumeratorも継承することになるため、IEnumeratorのメソッドも実装する必要がある。
internal class MyCollection<T>:IEnumerable<T>
{
private List<T> elems;
internal MyCollection(int num)
{
elems = new List<T>(num);
}
public IEnumerator<T> GetEnumerator()
{
foreach (var elem in elems) yield return elem;
}
public IEnumerator GetEnumerator()
{
return GetEnumerator();
}
}
【C#】IEnumerable<T>
IEnumerable<T>インタフェース
IEnumerator<T>インタフェースを返す抽象メソッドGetEnumerator()を持つ。
【C#】IEnumerableインタフェースの実装
IEnumerableインタフェースの実装
に記載の通り、IEnumerableインタフェースは抽象メソッドGetEnumerator()を持っているため、IEnumerableインタフェースを実装する際には、GetEnumerator()を実装する必要がある。
internal class MyCollection : IEnumerable
{
private readonly int[] m_array;
internal MyCollection(int number)
{
m_array = new int[number];
for (int i = 0; i < number; ++i)
{
m_array[i] = i;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
// IEnumeratorインタフェースを返す処理
}
}
GetEnumerator()の実装
IEnumerator IEnumerable.GetEnumerator()
{
return m_array.GetEnumerator();
for (int i = 0; i < m_array.Length; ++i)
{
yield return m_array[i];
}
foreach (var item in m_array)
{
yield return item;
}
}