2011-07-27 25 views
3

在C++中,我可以重置操作符的函數指針嗎?在C++中,我可以重置操作符的函數指針嗎?

特別是我想設置成員函數operator []使用(或不使用)邊界檢查。我試過這個沒有運氣:

這甚至有可能嗎?如果是這樣,任何人都可以糾正語法?

在MyArrayClass.h:

class MyArrayClass { 
public: 
    bool CheckArrayBounds; 
    float BoundsCheck(int i) { 
     if (i<_size) 
      return _data[i]; 
     return TCpx(_INVALID); 
    } 
    float NoBoundsCheck(int i) { 
     return _data[i]; 
    } 
    void UseBoundsCheck(bool State) { 
     if (State) { 
      float (operator[]) = &MyArrayClass::BoundsCheck; 
     } else { 
      float (operator[]) = &MyArrayClass::NoBoundsCheck; 
     } 
     CheckArrayBounds = State; 
    } 
    float operator[](int i) { return _data[i]; }; 
}; 
+1

而不是使用一個條件,你可以像'std :: vector'那樣通過'operator []()'和一個函數'at()'來做到這一點,它們都做同樣的事情。除了'operator []()'沒有邊界檢查和'at()'邊界檢查。 –

+1

難道你不能像std :: vector一樣使用STL容器嗎?您是否試圖實現STL容器中不可用的某些功能? – Gob00st

+0

運算符通常是內聯定義的(就像這裏),因此通常並不真正「被調用」,而是取而代之的是它們被使用的地方。這發生在編譯時,所以它的定義不可能在運行時改變。但是,當然,您可以根據某些變量更改運算符[]的行爲以執行邊界檢查或不執行邊界檢查。唯一的辦法是,可以通過共享庫來完成像我改變函數指針那樣的事情。 –

回答

6

這是不可能的。從語義上講,成員函數不是函數指針(儘管它們可以在引擎蓋下以這種方式實現)。

您可以在operator[]()內執行State的檢查,或使用代理對象。

1

方法(和它們是方法/函數只是語法糖運營商)是在編譯時無法運行時間定義的。

2

您不能在運行時更改特定對象實例的運算符,因爲成員函數與對象屬性分開存儲,並且對於所有對象實例都是相同的。它們存儲在寫保護的內存段中。或者甚至可以由編譯器內聯。

仍然可以檢查運營商[]實施內部的狀態。

您也可以創建屬性at並替換運算符。

class MyArrayClass { 
public: 
    float (MyArrayClass::*at)(int i); 
    float BoundsCheck(int i); 
    float NoBoundsCheck(int i); { 
    void UseBoundsCheck(bool State) { 
     at = (State)? &MyArrayClass::BoundsCheck : &MyArrayClass::NoBoundsCheck; 
    } 
}; 

用法:

MyArrayClass a; 
a.at(1); 
1

C++是不正確的一種語言這個的,很遺憾。你需要一個更強大的類型系統。

請注意,即使你可以,你可能會減慢你的程序的速度:整數比較比通過指針調用函數要昂貴得多。特別是因爲你的CPU可以預測分支:它會像運行過檢查一樣開始運行代碼,如果它最終失敗了,它可能會拋棄它所做的任何事情。

但請注意你的編譯器是聰明的。例如,如果我們有這樣的:

#include <algorithm> 
#include <cstdlib> 
#include <iostream> 
#include <iterator> 
#include <vector> 

void process(std::vector<int>& data) 
{ 
    for (unsigned i = 0; i < data.size(); ++i) 
    { 
     int v = data.at(i); 
     std::cout << v << std::endl; 
    } 
} 

int main() 
{ 
    try 
    { 
     std::vector<int> data; 
     std::generate_n(std::back_inserter(data), std::rand() + 1, std::rand); 

     process(data); 
    } 
    catch (...) 
    { 
     std::cerr << "nope.avi" << std::endl; 
    } 
} 

如果我們用g++ -O3編譯,有沒有邊界檢查代碼。事實上,編譯器已經推斷出at()裏面的檢查永遠不會通過(然後拋出),所以它剝離了那個代碼。所以你可以離開邊界檢查,你的編譯器仍然可以剝離它。但是,請注意,任何更復雜的情況都可能使編譯器難以證明,因此您將爲此付費。

這些是您可以通過更具表現力的類型系統保證的優化類型,但編譯器無論如何都可以完成這些優化。我不知道MSVC;它往往不那麼聰明,但你可以檢查。

你最好打的是去std::vector<>路線:提供一個未檢查的operator[]和一個檢查at()。讓你的班級的用戶決定他們是否需要檢查。

0

函數和成員函數實際上是不變的,是不可變的,在運行時不可能改變它們。然而,對於你正在嘗試做的事情,有一種半途而廢的房子,而你幾乎在你的發佈代碼中。

你可以做的是定義一個指向成員函數的指針,然後在運行時改變指針的值,這取決於你想要的邊界檢查是否有效。

class MyArrayClass { 
public: 
    typedef float (MyArrayClass::*AccessFn)(int i); 
    AccessFn Access; 
    bool CheckArrayBounds; 

    float BoundsCheck(int i) { 
     if (i<_size) 
      return _data[i]; 
     return TCpx(_INVALID); 
    } 
    float NoBoundsCheck(int i) { 
     return _data[i]; 
    } 

    MyArrayClass() { UseBoundsCheck(false); } 

    void UseBoundsCheck(bool State) { 
     if (State) { 
      Access = &MyArrayClass::BoundsCheck; 
     } else { 
      Access = &MyArrayClass::NoBoundsCheck; 
     } 
     CheckArrayBounds = State; 
    } 
    float operator[](int i) { return (this->*Access)(i); } 
}; 

這是做你所要求的,我能想到的最接近的方式。

+0

感謝您的幫助。我最初嘗試你的方法沒有成功,因爲我有運算符[]的語法錯誤: float operator [](int i){return Access(i); }; // < - - BAD SYNTAX 我一直希望避免Access()函數所需的間接尋址,因爲operator []在循環中被稱爲LOT,並且這對於不保證支持STL的嵌入式系統。 所以,我在代碼中添加了一個#define來構建它。 再次 - 感謝您在考慮此問題的時間。 –

相關問題