nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

Qt5の学習

QTまとめページ とても親切に、最初から説明されているページです。 まず、QTを勉強するならこのページを見るのがベストだと思います。 qt-log.open-memo.net

Qtの概要は、以下のページが参考になります。

Qtプログラミング入門 | densan-labs.net

SIGNALの説明やSLOTの説明は、以下のサイトに記載してあります。 https://blog.qt.io/jp/2010/07/20/create-signals-and-slots-2/

[connect 関数]

  • connect(sender, SIGNAL(signal), receiver, SLOT(slot) );
    • sender : 信号が発生する部品のアドレスを渡す
    • SIGNAL(signal) : signal に信号とする関数を渡す
    • receiver : 信号を受け取る部品のアドレスを渡す
    • SLOT(slot) : 信号を受け取った際に呼び出す関数を渡す

[connect関数の使い方]

#include "mainwindow.h"
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QPushButton* button = new QPushButton("Quit");
    QObject::connect(button, SIGNAL( clicked() ),
            &app, SLOT(quit()) );
    button->show();
    return app.exec();
}

コードレビューで指摘していただいた箇所まとめ (C++編)

コードレビューで指摘していただいた箇所をまとめて記載したいと思います。

  • ヘッダ内では、極力他のヘッダをインクルードしないようにしましょう
    • 2重インクルードや、循環参照(*1)を引き起こします
  • クラス名、メソッド名、変数名をしっかり考えよう
    • 例えば、犬クラスは、犬という情報を表現するクラスになっていますか?犬クラスなのに、犬クラスの中にスマートフォンを操作するメソッドがあってはいけません。( ´∀` )
  • コーディングスタイルが統一しましょう
    • スペース
    • 中かっこの位置
  • for文(繰り返し文)の中に、繰り返すたびに呼び出す必要がないのに、呼び出している処理はありませんか。
    • for文の外に処理を映しましょう
  • constが使えるのに、constを使っていない変数、メソッド引数はありませんか。
    • constが使える場合には、constを使いましょう
  • ポインタ渡しと参照渡し、どちらでも実現できる場合は、参照渡しにしたほうが良いと思います
    • 参照渡しは、実体がなければ実施できないため、呼び出し先のメソッドで、NULLポインタチェックが必要にならない

(*1) 循環参照問題とは、ヘッダーファイルAがヘッダーファイルBをインクルードし、ヘッダーファイルBがヘッダーファイルAをインクルードする事

コードレビューの観点まとめ

今回は、コードレビューの観点まとめについて、記載していきたいと思います。

<コードレビューでプラス事項>

  • 誰がひきついでもわかりやすく、読みやすいコード
  • 変数名や関数名が工夫されていてコメントが少なく読みやすいコード
  • 変数のスコープが必要最低限になっているコード
  • constとconstexprを適切に使って定数と変数が区別されているコード、インスタンス変数を書き換えないメソッド
  • 問題を解くためのデータ構造とアルゴリズムが隠蔽されていて、その2つを変更してもユーザーコードの変更がいらないように設計されているクラス
  • 使用変更や機能拡張が容易なコード
  • assertやStatic_assertが適切に使われており、事前条件を確認し、契約によるプログらイングができているコード
  • 再利用性が高くなるように責務分割されたコード

<コードレビューでマイナス事項>

  • 1つの関数であれもこれもやっている関数。凝縮度が低い関数。
  • ファイル分割されていないプログラム
  • ファイル分割のねらい、基準がはっきりしないプログラム
  • ユーザーにとってインタフェースが使いやすく設計されていないクラス。 (ユーザーコードがきれいに書けないインタフェース)
  • 階層化されておらず、レベルの違う処理を順番に読み出すだけの関数

独自クラスをstd::vectorに格納する

std::vector に独自クラスを入れる場合、デフォルトコンストラクタとコピーコンストラクタが必要不可欠みたいです。

d.hatena.ne.jp

コピーコンストラクタについては、このページに詳しく書かれています。 http://nobunaga.hatenablog.jp/entry/2016/07/03/230337

以下にサンプルコードを記載します。

#include <iostream>
#include <vector>

class Foo {
public:
    int i;
    Foo() : i(0) {
        std::cout << "default" << std::endl;
    }
    Foo(int num) : i(num) {
        std::cout << "int param" << std::endl;
    }
    Foo(const Foo &rhs) : i(rhs.i) {
        std::cout << "copy const" << std::endl;
    }
    ~Foo() {
        std::cout << "destructer" << std::endl;
    }
};

int main(int argc, char *argv[]) {
    std::vector<Foo> v;
    v.push_back(Foo(1));
    v.resize(2);
    return 0;
}
#include <iostream>
#include <string.h>
#include <vector>       // ヘッダファイルインクルード
using namespace std;

// n r   #箱の数n, ボールの半径r 表す整数
// h_1 w_1 d_1   #1個目の箱の高さ、幅、奥行きを表す整数
// h_2 w_2 d_2   #2個目の箱の高さ、幅、奥行きを表す整数
// ...
//h_n w_n d_n   #n個目の箱の高さ、幅、奥行きを表す整数


class Ball {
    
    int height;
    int width;
    int depth;
    
public:
    Ball();
    
    Ball(int h, int w, int d){
        height = h;
        width = w;
        depth = d;
    }
    
    int getHeight(){return height;}
    int getWidth(){return width;}
    int getDepth(){return depth;}
    
    // コピーコンストラクタ
    Ball (const Ball& rhs) : height(rhs.height), width(rhs.width), depth(rhs.depth){
        
    }
};

int main() {
    
    // パラメータ取得取得処理
    char str[100];
    char *pcError = NULL;
    
    // 1行目のデータ取得処理
    if (fgets(str, sizeof(str), stdin) == NULL) {
        return 0;
    }
    
    char delim[]=" \t\r\n\v\f"; 
        
    // strtok関数の初回の呼び出しは、第一引数に、分解対象の文字列を指定すること
    int numItem = strtol(strtok(str, delim), &pcError, 10);
    if (*pcError != '\0') {
        printf("strtoul() error: %s\n", str);
        return 0;
    }
    
    // strtok関数の2回目以降の呼び出しは、第一引数に、NULLを指定すること
    const int radiusBall = strtol(strtok(NULL, delim), &pcError, 10);
    if (*pcError != '\0') {
        printf("strtoul() error: %s\n", str);
        return 0;
    }
    printf("numItem = %d, raidusBall = %d\r\n", numItem, radiusBall);
    
    std::vector<Ball> v;
    
    // 2行目以降のデータ取得処理
    for(int i = 0; i < numItem; i++) {   
        // 1行分文字列を読み出す
        if (fgets(str, sizeof(str), stdin) == NULL) {
            break;
        }
        // strtok関数の初回の呼び出しは、第一引数に、分解対象の文字列を指定すること
        const int height = strtol(strtok(str, delim), &pcError, 10);
        if (*pcError != '\0') {
            printf("strtoul() error: %s\n", str);
            return 0;
        }
        // strtok関数の2回目以降の呼び出しは、第一引数に、NULLを指定すること
        const int radius = strtol(strtok(NULL, delim), &pcError, 10);
        if (*pcError != '\0') {
            printf("strtoul() error: %s\n", str);
            return 0;
        }
        // strtok関数の3回目以降の呼び出しは、第一引数に、NULLを指定すること
        const int depth = strtol(strtok(NULL, delim), &pcError, 10);
        if (*pcError != '\0') {
            printf("strtoul() error: %s\n", str);
            return 0;
        }
        v.push_back(Ball(height, radius, depth));
    }
    
    int count = 0;
    for (Ball x: v) {
        count++;
        if(x.getHeight() >= radiusBall && x.getWidth() >= radiusBall && x.getDepth() >= radiusBall) {
               cout << count << endl;
        }
    }
}

MFCで、ファイル選択ダイアログを学習する

MFCで、ファイル選択ダイアログを学習します。 開発環境は、Visual Studio 2015 Communityです。

■単一選択ダイアログと複数選択ダイアログを作成します。

(1) ダイアログベースでプロジェクトを作成し、次のようなダイアログを作成します。 f:id:nprogram:20170612195351p:plain

(2) エディットボックスには、m_xvEditFileの変数を追加します。

f:id:nprogram:20170612194332p:plain

(3) リストボックスには、m_xcListFileの変数を追加します。 f:id:nprogram:20170612200648p:plain

(4) ボタンには、それぞれ、ボタンクリックイベントハンドラを追加します。

単一ファイル参照用のイベントハンドラメソッドには、以下のように記載してください。

void CFileSelectDialogsDlg::OnBnClickedButton1()
{
    CString         filter("JPEG Files (*.jpg;*.jpeg)|*.jpg; *.jpeg||");
    CFileDialog     selDlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, filter);

    if (selDlg.DoModal() == IDOK)
    {
        m_xcListFile= selDlg.GetPathName();
        UpdateData(FALSE);
    }

    return;
}

複数ファイル参照用のイベントハンドラメソッドには、以下のように記載してください。

void CFileSelectDialogsDlg::OnBnClickedButton2()
{
    CString         filter("JPEG Files (*.jpg;*.jpeg)|*.jpg; *.jpeg||");
    CString         filePath, strBuf;
    POSITION        pos = NULL;
    CFileDialog     selDlg(TRUE, NULL, NULL,
        OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT, filter);
    int             err = 0, lbErr = 0;

    // ファイル名リスト用メモリ確保
    if (!err)
    {
        try
        {
            selDlg.GetOFN().lpstrFile = strBuf.GetBuffer(MAX_PATH * 100);
            selDlg.GetOFN().nMaxFile = MAX_PATH * 100;
        }
        catch (...) { err = 1; }
    }
    if (!err) if (selDlg.DoModal() != IDOK) err = 1;
    if (!err) if ((pos = selDlg.GetStartPosition()) == NULL) err = 1;
    if (!err)
    {
        while (pos)
        {
            filePath = selDlg.GetNextPathName(pos);
            if (!err)
            {
                lbErr = m_xcListFile.InsertString(-1, filePath);
                if (lbErr == LB_ERR || lbErr == LB_ERRSPACE) err = 1;
            }
            if (err) break;
        }
        UpdateData(FALSE);
    }
    strBuf.ReleaseBuffer();

    return;
}

以下のように、表示されれば、成功です。

<単一ファイル参照用のダイアログボックス表示>

f:id:nprogram:20170612201325p:plain

<複数ファイル参照用のダイアログボックス表示>

f:id:nprogram:20170612201416p:plain

<ファイル参照結果> f:id:nprogram:20170612201538p:plain

■本ページの記載は、以下のホームページを参考にしました。 ありがとうございます。

http://www.g-ishihara.com/mfc_cd_01.htm

■CFileDialog クラスのドキュメントは、以下でCFileDialogで、検索してください。

https://msdn.microsoft.com/ja-jp/library/

MFCアプリケーションの学習

MFCアプリケーションの学習をする必要が発生したため、記録します。

開発環境は、Visual Studio Community 2015です。

まずは、プロジェクト作成方法です。

ファイル⇒プロジェクトを選択し、MFCアプリケーションを選択します。プロジェクト名は、わかりにくくて、申し訳ないのですが、MFCApplication2としました。

f:id:nprogram:20170612105640p:plain

アプリケーションの種類で、ダイアログベースを選択してください。

f:id:nprogram:20170612105706p:plain

あと、そのまま、デフォルト設定でOKボタンを押します。

プロジェクト作成が完了すると、次のような画面が表示されます。

f:id:nprogram:20170612110300p:plain

■ PlaySound関数を用いた音楽再生サンプルアプリケーションを作成します。

・PlaySound関数を用いた簡単な音楽再生アプリケーションを作成します。 ただし、このアプリケーションは、wavファイルしか再生できず、また、音楽の途中からは再生できません。(泣)

■プログラム表示イメージ f:id:nprogram:20170612104610p:plain

(1) ソリューションエクスプローラーから、リソースファイルを選択します。

f:id:nprogram:20170612110608p:plain

(2) リソースビューが開かれるので、DIALOGをダブルクリックします。

f:id:nprogram:20170612110733p:plain

(3) 次のような画面が開かれます。

f:id:nprogram:20170612111309p:plain

(4) 上記画面左のツールボックスを選択して、Edit Controlを選択して、ダイアログにドラック・アンド・ドロップします。

f:id:nprogram:20170612111509p:plain

(5) 画面にファイルダイアログを開くボタンを追加します。 ツールボックスで、Buttonを選択して、変数の追加を選択します。

f:id:nprogram:20170612111810p:plain

(6) メンバー変数の追加ウィザードは次のようにします。カテゴリはValueにしてください。

f:id:nprogram:20170612113035p:plain

(7) Visual Studioの画面右下のプロパティウィンドウのCaptionに...を入力します。

f:id:nprogram:20170612113218p:plain

(8) プロパティウィンドウの管理イベント(電撃マークみたいなアイコン)を選択してください。

f:id:nprogram:20170612113851p:plain

MFCApplication2Dlg.cppファイルにイベントハンドラメソッドが追加されるため、以下のようにコードを記載します。

void CMFCApplication2Dlg::OnBnClickedButton1()
{
    CFileDialog dlg(
        TRUE,       // FileOpen
        L".wav",
        L"",
        0,
        L"WAVファイル (*.wav)|*.wav|All Files (*.*)|*.*||",
        this);

    if (dlg.DoModal() == IDOK)
    {
        m_strFilePath = dlg.GetPathName();
        UpdateData(FALSE);
    }
}

(9) 一度、アプリケーションを実行しましょう。ビルドして、実行しましょう。...ボタンを押して、ダイアログが開かれることを確認してください。

f:id:nprogram:20170612114535p:plain

(10) 次に、再生ボタンと停止ボタンを画面に追加します。 リソースビューから、ツールボックスを選択して、Buttonを二つダイアログ上にドラック・アンド・ドロップします。 Buttonの表示文字列をプロパティウィンドウからPlay````とStop```に変更してください。

f:id:nprogram:20170612115027p:plain

(11) ```Play````のボタンを選択して、ボタンをクリックしたときのイベントハンドラメソッドを追加し、以下のように記載します。 音楽を再生するのに、PlaySound関数を用います。

void CMFCApplication2Dlg::OnBnClickedButton2()
{
    PlaySound(m_strFilePath, NULL, 0); // 非同期ではない処理
}

(12) 同様に、```Stop````のボタンを選択して、ボタンをクリックしたときのイベントハンドラメソッドを追加し、以下のように記載します。

void CMFCApplication2Dlg::OnBnClickedButton3()
{
    PlaySound(NULL, NULL, 0);
}

(13) PlaySound関数を使うには、mmsystem.hのヘッダファイルをインクルードし、winmm.libをリンクする必要があります。stdafx.h````に、インクルード文#include <mmsystem.h>```を追加してください。

f:id:nprogram:20170612120717p:plain

プロジェクトを右クリックして、プロジェクトのプロパティページを開いて、構成プロパティ⇒リンカーから、追加の依存ファイルにwinmm.libを記載します。 f:id:nprogram:20170612121622p:plain

(14) アプリケーションを実行しましょう。wavファイルをファイル選択ボタンから開き、再生ボタンを押して、音楽が再生されることを確認してください。 音楽は、再生できるようになりましたが、他のボタンを押すことができません。PlaySound関数が非同期処理になっていないためです。

f:id:nprogram:20170612121921p:plain

そこで、次のように、PlaySound関数の呼び出し方を修正して、非同期処理で音楽を再生します。

void CMFCApplication2Dlg::OnBnClickedButton2()
{
    PlaySound(m_strFilePath, NULL, SND_FILENAME | SND_ASYNC);   // 非同期処理 (音楽再生中に、Stopボタンが押せます)
}

(15) アプリケーションを実行しましょう。 wavファイルをファイル選択ボタンから開き、再生ボタンを押して、音楽が再生され、音楽再生中に、停止ボタンが押せれば、本サンプルアプリケーションは完成です。

古い技術ではありますが、引き続き、MFCの学習を続けていこうと思います。

なお、本ページの記載は、以下のサイトを参考にさせていただきました。 ありがとうございます。

・Cactus Software様 MFC C++アプリケーション

自作split関数を用いて、標準入力を、読み出す

プログラムをメモします。

#include <vector>
#include <iostream>
#define BIG_BUFFERSIZE 1024
using namespace std;

template <typename List>
void split(const std::string& s, const std::string& delim, List& result)
{
    result.clear();

    using string = std::string;
    string::size_type pos = 0;

    while(pos != string::npos )
    {
        string::size_type p = s.find(delim, pos);

        if(p == string::npos)
        {
            result.push_back(s.substr(pos));
            break;
        }
        else {
            result.push_back(s.substr(pos, p - pos));
        }

        pos = p + delim.size();
    }
}

int main(void)
{
  vector<string> strData;
  
    while(cin) {
        string input_line;
        getline(cin, input_line);
        cout << input_line << endl;
        strData.push_back(input_line);
    };
    
    
    for(auto i = strData.begin(); i != strData.end(); ++i) {
        vector<string> split_data;
        cout << *i << endl;
        split(*i, ".", split_data);
        
        for(auto j = split_data.begin(); j != split_data.end(); ++j) {
            cout << *j << endl;
        }
    }
}

入力

192.168.186.70
1
192.168.110.238 - - [10/Jul/2013:18:40:43 +0900] "GET /top.html HTTP/1.1" 404 8922 "http://gi-no.jp" "Mozilla/5.0"

出力

192.168.186.70
1
192.168.110.238 - - [10/Jul/2013:18:40:43 +0900] "GET /top.html HTTP/1.1" 404 8922 "http://gi-no.jp" "Mozilla/5.0"

192.168.186.70
192
168
186
70
1
1
192.168.110.238 - - [10/Jul/2013:18:40:43 +0900] "GET /top.html HTTP/1.1" 404 8922 "http://gi-no.jp" "Mozilla/5.0"
192
168
110
238 - - [10/Jul/2013:18:40:43 +0900] "GET /top
html HTTP/1
1" 404 8922 "http://gi-no
jp" "Mozilla/5
0"