指向成員的指針不是很用,但它們非常強大,你怎麼使用它們以及你做過的最酷的事情是什麼?指向成員和成員函數的最佳用法是什麼?
編輯: 這與其說是列出的東西都是可能的,例如上市的boost ::綁定和的boost ::功能並不好。相反,也許他們有一個很酷的用法?我知道他們自己很酷,但這不是這個意思。
指向成員的指針不是很用,但它們非常強大,你怎麼使用它們以及你做過的最酷的事情是什麼?指向成員和成員函數的最佳用法是什麼?
編輯: 這與其說是列出的東西都是可能的,例如上市的boost ::綁定和的boost ::功能並不好。相反,也許他們有一個很酷的用法?我知道他們自己很酷,但這不是這個意思。
我曾經需要使用標準數據作爲純結構來操作,以便能夠將所有標準列表存儲在隊列中。我必須將結構與GUI和其他過濾元素等綁定在一起。所以我想出了使用指向成員的指針以及指向成員函數的解決方案。
假設你有一個
struct Criteria
{
typedef std::string Criteria::* DataRefType;
std::string firstname;
std::string lastname;
std::string website;
};
比你可以用標準的領域,並與現場的字符串表示綁定
class Field
{
public:
Field(const std::string& name,
Criteria::DataRefType ref):
name_(name),
ref_(ref)
{}
std::string getData(const Criteria& criteria)
{
return criteria.*ref_;
}
std::string name_;
private:
Criteria::DataRefType ref_;
};
然後你就可以註冊所有領域使用你只要想要:GUI,序列化,按字段名稱進行比較等。
class Fields
{
public:
Fields()
{
fields_.push_back(Field("First Name", &Criteria::firstname));
fields_.push_back(Field("Last Name", &Criteria::lastname));
fields_.push_back(Field("Website", &Criteria::website));
}
template < typename TFunction >
void forEach(TFunction function)
{
std::for_each(fields_.begin(), fields_.end(),
function);
}
private:
std::vector<Field> fields_;
};
通過調用例如fields.forEach(serialization);
或
GuiWindow(Criteria& criteria):
criteria_(criteria)
{
fields_.forEach(std::bind1st(
std::mem_fun(&GuiWindow::bindWithGui),
this));
}
void bindWithGui(Field field)
{
std::cout << "name " << field.name_
<< " value " << field.getData(criteria_) << std::endl;
};
那麼我使用標準算法定期使用指向成員函數的指針。就我而言,他們沒有什麼特別之處。
指向成員函數非常適合用的for_each
創建僞蘭巴表達式vector<SomeClass*> v = getAVector();
for_each(v.begin(), v.end(), mem_fun(&SomeClass::print));
可以綁定成員變量和函數使用的boost ::綁定,讓平常函子。
與他們接下來的工作將喜歡在通常的功能的使用:
除了上述之外,您可以將它們用作回調函數。
我和他們做過的最酷的事情,我很久以前做過。今天可能有更好的方法。
我爲網絡管理工具創建了一個自生成的命令行解析器。表示要管理的對象的類每個都有自己的子類表(名稱,指向工廠成員的指針),實例(id,實例從列表中的指針)以及命令(名稱,指向成員函數的指針)。這使得解析器來處理喜歡的事物:
SET NETWORK ROUTE 192.168.0.0 HOPS 1
或
QUERY NETWORK NAMESERVER servername
無需瞭解路線,或域名服務器什麼。
我做了一個「DomainEditor」類這個龐大的應用程序,我寫的。數據庫中的所有類型(域)表都可以由程序的管理員編輯,並且由於客戶端通過與其他名稱不同的名稱調用某些類型,我做了一個對話框,允許您編輯它們。好吧,我不想爲15種以上的域類型編輯一個編輯器,所以我編寫了一個可以將每個類轉換爲的超類,並且使用指針可以對每個域表進行簡單調用。每個人都支持所有相同的屬性,描述(名稱),ID,非活動標誌和必需標誌。所以,代碼開始與宏設置我的電話:
#define DomainList(Class, Description, First, Next, Item, UpdateItem, DeleteItem, IsItemRequired, MaxLength) { \
CWFLHandler *handler = new CWFLHandler; \
handler->pWFL = new Class;\
handler->LoadFirstType = (LoadFirst)&Class::First;\
handler->LoadNextType = (LoadNext)&Class::Next;\
handler->LoadType = (Load)&Class::Item;\
handler->UpdateType = (Update)&Class::UpdateItem;\
handler->DeleteType = (Delete)&Class::DeleteItem;\
handler->IsRequiredType= (IsRequired)&Class::IsItemRequired; \
handler->MAX_LENGTH = MaxLength;\
PopulateListBox(m_Domain, Description, (long)handler); }\
然後,大量的調用宏:(這裏只是一個單一的一個)
DomainList(CConfigWFL, "Application Parameter Types", LoadFirstParameterType, LoadNextParameterType, LoadParameterTypeByTypeId, UpdateParameterType, DeleteParameterType, IsParameterTypeRequired, LEN_APPL_PARAMETER_DESC);
然後,調用編輯數據都是常見的,我根本不需要複製任何代碼...
例如,要使用DropDownList中的選定項目填充列表(由宏填充),代碼將顯示像這樣:
if((pWFLPtr->pWFL->*pWFLPtr->LoadFirstType)(true))
{
do
{
m_Grid.AddGridRow();
m_Grid.SetCheck(COLUMN_SYSTEM, (pWFLPtr->pWFL->*pWFLPtr->IsRequiredType)(pWFLPtr->pWFL->TypeId));
m_Grid.SetCheck(COLUMN_STATUS, pWFLPtr->pWFL->InactiveIndc == false);
m_Grid.AddTextToGrid(COLUMN_NAME, pWFLPtr->pWFL->TypeDesc);
m_Grid.AddTextToGrid(COLUMN_DEBUG, pWFLPtr->pWFL->TypeId);
m_Grid.AddTextToGrid(COLUMN_ID, pWFLPtr->pWFL->TypeId);
}
while((pWFLPtr->pWFL->*pWFLPtr->LoadNextType)());
當然,這些都存儲在對話框的一部分。我只是創建了類的新實例,並將它們存儲在ListBox的ItemData成員中。所以,當對話結束時,我確實必須清理所有這些。但是,我將該代碼從此消息中刪除。
來存儲所有這些東西在課堂上被定義爲:
typedef bool (CMyWFL::*LoadFirst)(bool);
typedef bool (CMyWFL::*LoadNext)();
typedef bool (CMyWFL::*Load)(long);
typedef bool (CMyWFL::*Update)(long, const char*, bool);
typedef bool (CMyWFL::*Delete)(long);
typedef bool (CMyWFL::*IsRequired)(long);
class CWFLHandler {
public:
CWFLHandler() {};
~CWFLHandler() { if(pWFL) delete pWFL; }
CMyWFL *pWFL;
LoadFirst LoadFirstType;
LoadNext LoadNextType;
Load LoadType;
Update UpdateType;
Delete DeleteType;
IsRequired IsRequiredType;
int MAX_LENGTH;
};
CWFLHandler *pWFLPtr;
所有這些工作使得它真的很高興能夠將新域添加到應用程序很少的工作,將其添加到域編輯...可能有更好的辦法,我不知道。但這是我去的方式,它對我很好,恕我直言,這是非常有創意... :)
我用它們作爲StructSerlialiser的一部分來填充SAX解析器事件的C++ POD結構,即將XML映射到C++數據模型。
template<class STRUCT, typename FIELDTYPE>
struct FieldBinderImpl : public FieldBinder<STRUCT>
{
typedef FIELDTYPE (STRUCT::*MemberPtr);
FieldBinderImpl (const std::string& tag, MemberPtr memberPtr)
: FieldBinder (tag)
, memberPtr_ (memberPtr)
{
}
virtual SerialiserBase* createSerialiser (STRUCT& data) const
{
return new Serialiser<FIELDTYPE> (&(data.*memberPtr_));
}
private:
MemberPtr memberPtr_;
};
template<class T>
class StructSerialiser : public SerialiserData<T>
{
public:
typedef std::vector<FieldBinder<T>*> FieldBinderList;
private:
static FieldBinderList fieldBinderList_;
protected:
template<typename FIELDTYPE>
static void bind (const std::string& tag, FIELDTYPE (T::* member))
{
fieldBinderList_.push_back (new FieldBinderImpl<T, FIELDTYPE> (tag, member));
if (tag.empty())
fieldBinderList_.back()->tags_ = Serialiser<FIELDTYPE>::getTags();
}
// ...
}
// ...
也有串行器雙,字符串,向量等。要使用它,你只想綁定結構成員的名字,例如:
class Index
{
public:
std::string currency;
std::string name;
};
template<>
class Serialiser<Index> : public StructSerialiser<Index>
{
public:
Serialiser (Index* data) : StructSerialiser<Index> (data) {}
static void initialise()
{
bind ("currency", &Index::currency);
bind ("name", &Index::name);
}
};
你的意思是mem_fun_ref,不是嗎? – dirkgently 2009-04-08 18:29:17