C/C++ Programing: static形態之成員函式/變數

前言

偷學過來的招式

一開始只是覺得很酷。

之前在寫程式就遇上這樣的需求,"有一份資源,然後許多Class都需要能夠取得",解法依照情況其實有很多種,而當時我發現的就是把class內的成員函式/資料設定爲static。

用遊戲的例子來說,就是視窗只有一個,但是取用視窗這個資源(清除畫面內容,跟更新畫面內容)的class卻有好幾份。過去我有寫過很畸形的code,"在每次創造這些物件的時候,放入視窗的相關資訊"。不過醜到自己都受不了,上google+剛好看見有人在展示自己的程式,於是就拿起來看一看,學習了一番。

下面的例子都使用g++編譯

看看怎麼用

假設今天我有一份*.h檔案長這樣:

window.h
#include <iostream>
#include <SDL.h>

class Window
{
    public:
    Window();
    ~Window();
    
    //Static functions
    static void Clear();
    static void Present();
    
    //Static variables
    static SDL_Window* mainWindow;
};

那麼在任何檔案內,只要有宣告#include "window.h",就可以取用到window.h內宣告的static函式/變數。使用方法像是下面這樣:

a.cpp
#include "window.h"
...
Window::mainWindow = SDL_CreateWindow(...);
...

class中static形態的成員,都只會存在一份,所以即使創造了許多Window物件,這些static形態的資料依然是相同的,這樣的特性可以拿來記錄某物件被產生了幾個,幫每個物件上編號等神奇的功能。而且這些static變數依然可以被其他非static函式所取用,下面來看個更完整的例子。

細節

我不擅長解說,所以把解釋放在註解中。

test.h
#include <iostream>

class Test
{
    public:
    Test(){};
    ~Test(){};
    
    //Static 一夥
    static void static_getCount();
    static int static_count;
    
    //普通一夥
    void normal_getCount()
    
  private:
    //static,卻在private中,想也知道外面摸不到
     static int static_privateNum;
};
test.cpp
#include "test.h"

/*
一定要class外給static變數在宣告一次
如果不宣告,鏈接時期會找不到Test::static_count跟Test::static_privateNum
詳細的編譯理由,尚在尋找
*/
int Test::static_count = 0;
int Test::static_privateNum = 123;

//static形態的成員函式,其宣告和一般成員沒有差別
void Test::static_getCount()
{
    //static函式取用static成員,沒問題
 cout << "count= " << static_count << endl;
}

//普通的成員函式
void Test::normal_getCount()
{
    //static_count依然可以被其他普通的成員函式所取用
 cout << "count= " << static_count << endl;
}
main.cpp
#include <iostream>
#include "test.h"

//對不起,我有點懶
using namespace std;

int main(int argc, char* argv[])
{
  //可以對其做賦值
  Test::static_count++;
  
  //ERROR!private成員只有該class內的成員函式可以存取,哪怕他是static變數
  Test::static_privateNum++;

    //可以直接呼叫
  Test::static_getCount();
  
  //Error!normal_getCount不是static的成員函式
  Test::normal_getCount();
  
  //必須先創造出一個Test物件,才能夠正確取用normal_getCount
  Test test;
  test::normal_getCount();
}

C語言的struct也行 搞錯了,這個還是c++

在查詢相關資料時發現,C語言中的structure居然也有和class幾乎相同的特性!(對不起我之前說C沒辦法作出物件) 過去我壓根沒想過可以把函式的定義塞到struct裏面。

烏龍一場,這邊談到的struct特性和行爲,是指c++中的struct

用上面的例子簡單改寫,產生出像下面這樣:

test.h
#include <stdio.h>
#include <stdlib.h>

struct Test
{
    /*
  constructor在C語言是合法的存在,行爲和class相同
  都是在建立struct Test物件時被呼叫一次
  
  宣告destructor則會出現找不到參考的錯誤訊息
  
  但是考量到有動態配置的需求,通常會額外建立一個constructor
  因爲使用malloc配置struct Test資料空間時,並不會觸發Test()這道函式
  */
  Test();
    
    /*自己做的constructor*/
    static struct Test* constructor();
    
    /*自己做的destructor*/
    static void destructor(struct Test* target);
    
    /*Static 一夥*/
    static int static_getCount();
    static int static_count;
};
test.c
#include "test.h"

/*同樣,需要再宣告一次*/
int Test::static_count = 0;

/*定義實作的方式和C++的class相同*/
Test::Test()
{
    static_count++;
}

struct Test* Test::constructor()
{
    static_count++;
  
  struct Test* tmp = (struct Test*)malloc(sizeof(struct Test));
  return tmp;
}

void Test::destructor(struct Test* target)
{
  static_count--;
  
  free(target);
  target = NULL;
}

int Test::static_getCount()
{
    /*這個函式其實是多此一舉,因爲static形態的變數本來就可以被外部成員所取用*/
    return static_count;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "test.h"

int main(int argc, char* argv[])
{
    printf("Before we do something\n");
    printf("static_count= %d\n\n", Test::static_count);
  
    struct Test normal_test;
    printf("Create normal struct Test\n");
    printf("static_count= %d\n\n", Test::static_count);
  
    struct Test* dynamic_test;
    printf("Create a pointer of struct Test\n");
    printf("static_count= %d\n\n", Test::static_count);
  
    dynamic_test = Test::constructor();
    printf("Using Test::constructor()\n");
    printf("static_count= %d\n\n", Test::static_count);
  
    Test::destructor(dynamic_test);
    printf("Using Test::destructor()\n");
    printf("static_count= %d\n\n", Test::static_count);
  
    return 0;
}
執行結果
Before we do something
static_count= 0

Create normal struct Test
static_count= 1

Create a pointer of struct Test
static_count= 1

Using Test::constructor()
static_count= 2

Using Test::destructor()
static_count= 1

應用

在class或是struct中塞入static變數,最簡單的應用就是像上面的範例,拿來記錄產生了多少的物件,能夠更有效的去管理一些動態產生的東西。

不過個人也才剛發現這個特性,沒做過多少東西,目前也只知道這個應用。

雜談

想寫彈幕遊戲/普通的STG一陣子了,子彈一直是我搞不定的部分。

一開始是動態產生子彈(記錄子彈的位置),有用過Link list去解,這解法暫時是沒問題。

後來是子彈的行走軌跡,以前是假設飛行方向,飛行速度,然後用三角函數算出每一個frame的x跟y軸速度,但由於行走速度是整數,這樣的做法會導致飛行角度的可能性非常少(舉例來說,如果我的子彈飛行速度是1pixel/frame,用三角函數就只會有上下左右四種方向),大概要先定義出飛行軌跡的方程式。

總之我想說的是,這個特性或許可以在STG的製作中派上用場!

註:本人對於鏈接時期跟編譯時期的行爲不熟悉,如果上面解說有錯誤,還請多多指導糾正

參考資料

Comments

comments powered by Disqus