LoginSignup
2
1

More than 5 years have passed since last update.

はじめてのC++【23日目】

Last updated at Posted at 2019-03-21

はじめに

今日はロベールとブラックジャックをやります。

BlackJack【1日目】

ここはクラスで作ったほうがいいの?とかここは構造体使ったほうがいいの?とか関数作ったほうが便利じゃない?とか、考えてもよくわからなかったので、main関数だけで処理を作った。しかし、まだ完成していなく、自分がカードを引いて21になったら負けというだけの段階。
まだ、マークとかj~kはないのもとして扱っているのでマークとかも取り入れたい。

コード

BlackJack.cpp
#include <iostream>
#include <array>
#include <vector>
#include <numeric>
#include <random>
int main(){
  constexpr char* hart = "♥";
  constexpr char* spade = "♠";
  constexpr char* clover = "♣";
  constexpr char* dia = "◆";
  char choose = 'd';//dで初期化
  int count = 0;//動的配列の要素数
  int card = 0;//自分で引いた数を格納する場所
  int sum = 0;//引いたカードの合計値を格納する
  std::vector<int> dealer = {0};//ディーラーの持ち札を入れる
  std::vector<int> myself = {0};//自分の持ち札を入れる
  std::uniform_int_distribution<int> dist(1,10);//i~10のランダム生成
  std::mt19937 mt{ std::random_device{}() };//ランダム作るときに必要

  std::cout << "BrackJackを始めます" << std::endl;

  while(1) {
  //myself[count] =

  card = dist(mt);//引いたカードの数を決定
  myself[count] = card;//動的配列の前から順番に引いたカードの値を代入
  sum += card;//引いたカードの数字を合計値に足す
  std::cout << "カードを一枚引きました。" << std::endl
            << "数字は" << card<< "です" <<std::endl
            << "合計値は" << sum <<"です" << std::endl;//std::accumulate(myself.begin(), myself.end(), 0)は使えなかった
  if(sum > 21){//合計値が21より大きくなると負け
    std::cout << "21より大きくなりました。あなたの負けです。" << std::endl;
    break;
  }
  else if(sum == 21)break;//合計値が21丁度になると強制的に終了
  std::cout << "引き続きカードを引きますか?(y/n)" << std::endl;//カードを引くかどうかの意思決定
  std::cin >> choose;//y/nをchoooseに代入させる
  if(choose == 'n') break;//もしnならカードを引くことを終了
  count++;//配列の要素数を1つ上げる。
  }

}

実行結果
スクリーンショット 2019-03-21 22.32.25.png

一日目を終えて
今日やってみた感想。
実際に自分の書いたコードを見ることでクラスをどのように作ればいいか見えてきた。
カードを引く動作はディーラと自分で同じなので基底クラスにvirtualつけることでなんとかできそう。

動的配列で、格納した値をstd::accumulate(myself.begin(), myself.end(), 0)で出力しようとしたが、出力結果がおかしくなったので、sumを使った。

クラスを作るって難しい。てか、使い所がいまいちわからない。関数でよくない?と思いがちになる。明日は、実際に関数とかクラスを作ってみようと思う。

ロベールのC++

07-06 純粋仮想関数

いままでStreamクラス(基底クラス)に仮想関数を使用するためだけに実装内容を書いてきた。しかし、これは無駄である。なので、純粋仮想関数を使うことで、このムダを省くことができる。

Stream.h
#ifndef STREAM_H_
#define STREAM_H_

//0以上の値を順次取得していく処理行うための基底クラス
class Stream {
public:
  double Get() const;//設定された値を取得
  bool Set();
public:
  double m_n; //現在の値
protected:
  virtual void SetBase() = 0;//純粋仮想関数
};

#endif
Stream.cpp
#include "Stream.h"
#include <iostream>
//設定された値を取得
double Stream::Get() const {
  return m_n;
}
//値を設定
bool Stream::Set() {
  SetBase();
  return m_n >= 0;
}
Pure1.cpp
#include "InputStream.h"
#include <iostream>

int main(){
  InputStream istream;

  std::cout << "> " << std::flush;
  istream.Set();
  std::cout << istream.Get() << std::endl;
}

同じように実行された。

基底クラスで実体がなくても派生クラスでオーバーライドしていればなにの問題もない。
しかし、純粋仮想関数があるとそのクラスのオブジェクトは作れない!

Stream stream;

こんなことができなくなるのか。
このようなクラスを「抽象クラス」という。
また、純粋仮想関数が一つでもオーバーライドされていなければ派生クラスも抽象クラスになる。
抽象クラスでは、「このクラス群はこんなかんじ」とクラスの抽象的な形を定義し、具体的な動作は定義しない。
そして、具体的な動作は、派生クラスで定義する。(has-a関係っぽい)

constやアクセス指定子、純粋仮想関数はコンパイル時にエラーを出してくれるので、積極的に使う。

終わりに

明日は「継承とコンストラクタ」が3節あるので一気に終わらせる。
おやすみなさい。

2
1
19

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
2
1