3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

純粋仮想関数と抽象クラス、スマートポインタ

Last updated at Posted at 2019-08-19

やりたいこと

親クラスで定義されたスマートポインタを引数に取る純粋抽象関数を子クラスで実装して使いたいが、やり方がよく分からなかったのでメモをする。
Sub1, Sub2とBaseクラスを継承したものを複数個用意し、Sub1同士、Sub2同士を比較する関数を持たせたいという場面を想定している。子クラスのインスタンスを使用する関数にてテンプレートで解決するのだが、IDEのオートコンプリート機能を使いたいので、テンプレートは出来れば使いたく無いという我儘からきた考えである。私の結論としては、テンプレートを使ったほうが良さそうということになった。このコードはdynamic_castを使っており安全でないらしいことと、スマートポインタを使っても生ポインタを取り出して使用している、といったことをして動かしているのでうまいコードではないような気がするが、これよりうまい方法が思いつかなかった。ちなみになぜdynamic_castは安全でないのかはまだ僕は知らない。

実験

最初のコード。生ポインタをcomp関数に入れると動くがスマートポインタを入れると動かない。


#include <iostream>
#include <memory>

using namespace std;

class Base {
public:
  int val = 0;
  virtual bool comp(const Base* obj) = 0;
  virtual bool comp(const unique_ptr<Base> &obj) = 0;
};


class Sub : public Base {
public:
  explicit Sub(int i){ this->val = i*2; }
  bool comp(const Sub *obj) {
    return this->val < obj->val;
  };
  bool comp(const Base *obj) override {
    return comp(dynamic_cast<const Sub*>(obj));
  };
  // コンパイルエラー
  bool comp(const unique_ptr<Sub> &obj) {
    return this->val < obj->val;
  };
  bool comp(const unique_ptr<Base> &obj) override {
    return comp(dynamic_cast<const unique_ptr<Sub>>(obj));
    // return comp(dynamic_cast<const unique_ptr<Sub>&>(*obj));
    // return comp(dynamic_cast<Sub*>(obj.get()));
  };
};


int main() {
  // 生ポインタを使うと動く
  Base *pr1 = new Sub(1);
  Base *pr2 = new Sub(2);
  cout << (pr1->comp(pr2) ? "true" : "false") << endl;

  // スマートポインタを使用したい
  unique_ptr<Base> p1 = make_unique<Sub>(Sub(1));
  unique_ptr<Base> p2 = make_unique<Sub>(Sub(2));

  cout << (p1->comp(p2) ? "true" : "false") << endl;
  // 生ポインタを入れると動く
  cout << (p1->comp(p2.get()) ? "true" : "false") << endl;
  return 0;
}

しかし、生ポインタを引数に取りたいので、ちょっと改善したらコンパイルは通るが実行時にエラーが起こった。

  bool comp(const unique_ptr<Base> &obj) override {
    // return comp(dynamic_cast<const unique_ptr<Sub>>(obj));
    return comp(dynamic_cast<const unique_ptr<Sub>&>(*obj));
    // return comp(dynamic_cast<Sub*>(obj.get()));
  }

実行するとエラーが発生した。

>>> true
>>> libc++abi.dylib: terminating with uncaught exception of >>> type std::bad_cast: std::bad_cast
>>> 
>>> Process finished with exit code 6

こちらが動くパターンだ。comp関数は引数にユニークポインタを受け取っているが、結局キャストするときは生ポインタを使いましょうということである。エラーになる理由がよくわかってないが、とりあえずこれで動くことは確認できた。生ポインタを外部から入れるよりは少しマシである。

  bool comp(const unique_ptr<Base> &obj) override {
    // return comp(dynamic_cast<const unique_ptr<Sub>>(obj));
    // return comp(dynamic_cast<const unique_ptr<Sub>&>(*obj));
    return comp(dynamic_cast<Sub*>(obj.get()));
  }

bool comp(const unique_ptr<Base> &obj)の中身を変更すると、コンパイルが通るようになったり、実行時にエラーを出さなったりしたが、どんどん生ポインタに近づいていった。動くコードではbool comp(const unique_ptr<Sub> &obj)は使用されていないから、しかたない。ユニークポインタを、代入された関数が終了するまで保持し続けるのであればこれでもいいのかもしれないと思った。

ちなみに、スマートポインタのキャストを行いたい人は私以外にも存在するをとは確認できている。
https://stackoverflow.com/questions/26377430/how-to-do-perform-a-dynamic-cast-with-a-unique-ptr

謝辞

同僚のエンジニアのMさんに色々教えてもらったので感謝。

3
2
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?