|
|
范圍廣闊啊。 1、地圖 從A到B,哪條路花費最少 / 哪條是最快的路線,如果身上只能花N$,那么應該選擇哪條路? 2、超文本 圖處理算法是搜索引擎的基本組成部分 3、電路 如“能否將此電路做在芯片上而不出現任何線路交叉” 4、調度 如何滿足給定約束,又節省時間 5、事務 如對通信線路的布線從而高效地處理通信;對市場購銷現金流的監測以便加強對市場實際情況的了解。 6、匹配 如應聘人員與單位機構的匹配 7、網絡 計算機網絡的維護,如何調整節點以便確保某些站點或連接不至于處于太“要害”的地位。 8、程序結構 如何最佳地為程序分配資源以便做到最高效?
值得研究。
書里面說的這個詞: 型別計算的邊界標記
NullType只有聲明沒有定義。
class NullType;這是為了表達“我不是個令人感興趣的型別”,可以作為“找不到型別”的消息標記。類似\0這樣。 EmptyType,就是一個空類
這是可被繼承的合法型別,可以作為template的缺省參數型別。
在編譯時刻,在Conversion類中產生兩個常數(編譯器幫忙計算)
template <class T,class U>
class Conversion
  {
//
public:
 enum { exists2Way = exists && Conversion<U,T>::exists };
 enum { sameType = false };
};一個是 exists2Way,表示是否可以兩個類型互相轉換, sameType 表示 T和U是否同一個類型。 不過,雖然書里這么說,我怎么都搞不懂為什么這樣可以,測試也是不對的,難道這個sameType的寫法還有別的奧妙? 不過下面這個偏特的寫法倒是比較容易理解:
template <class T>
class Conversion<T,T>
  {
public:
 enum { exists = 1,exists2Way = 1,sameType = 1 };
};這個測試是OK的。 有了這幾個常數,要決定兩個class之間是否存在繼承關系就比較容易了:
#define SUPERSUBCLASS(T,U) \
(Conversion<const U*, const T*>::exists && \
!Conversion<const T*, const void*>::sameType)如果U是public繼承自T,或者T和U是同一個類,那么SUPERSUBCLASS(T,U)傳回true,這里是把某個class視為自己的超類,更嚴謹的做法是:
#define SUPERSUBCLASS_STRICT(T,U) \
(SUPERSUBCLASS(T,U) && \
!Conversion<const T, const U>::sameType)即排除T與U是同一個類型的情況。 另外,加上 const 是為了 防止因為 const 而導致轉型失敗,對于已經是const的東西再const一次的話后面一次的const會忽略掉。 再,這個宏的名字很清晰,就是 超類--子類, 前面那個T是超類,U是子類,這個命名比 INHERITS要好。
簡而言之:explicit修飾的構造函數不能擔任轉換函數
這個 《ANSI/ISO C++ Professional Programmer's Handbook 》是這樣說的
explicit Constructors A constructor that takes a single argument is, by default, an implicit conversion operator, which converts its argument to an object of its class (see also Chapter 3, "Operator Overloading"). Examine the following concrete example: class string { private: int size; int capacity; char *buff; public: string(); string(int size); // constructor and implicit conversion operator string(const char *); // constructor and implicit conversion operator ~string(); }; Class string has three constructors: a default constructor, a constructor that takes int, and a constructor that constructs a string from const char *. The second constructor is used to create an empty string object with an initial preallocated buffer at the specified size. However, in the case of class string, the automatic conversion is dubious. Converting an int into a string object doesn't make sense, although this is exactly what this constructor does.
Consider the following: int main() { string s = "hello"; //OK, convert a C-string into a string object int ns = 0; s = 1; // 1 oops, programmer intended to write ns = 1, } In the expression s= 1;, the programmer simply mistyped the name of the variable ns, typing s instead. Normally, the compiler detects the incompatible types and issues an error message. However, before ruling it out, the compiler first searches for a user-defined conversion that allows this expression; indeed, it finds the constructor that takes int. Consequently, the compiler interprets the expression s= 1; as if the programmer had written s = string(1); You might encounter a similar problem when calling a function that takes a string argument. The following example can either be a cryptic coding style or simply a programmer's typographical error. However, due to the implicit conversion constructor of class string, it will pass unnoticed: int f(string s); int main() { f(1); // without a an explicit constructor, //this call is expanded into: f ( string(1) ); //was that intentional or merely a programmer's typo? } 'In order to avoid such implicit conversions, a constructor that takes one argument needs to be declared explicit: class string { //... public: explicit string(int size); // block implicit conversion string(const char *); //implicit conversion ~string(); }; An explicit constructor does not behave as an implicit conversion operator, which enables the compiler to catch the typographical error this time: int main() { string s = "hello"; //OK, convert a C-string into a string object int ns = 0; s = 1; // compile time error ; this time the compiler catches the typo } Why aren't all constructors automatically declared explicit? Under some conditions, the automatic type conversion is useful and well behaved. A good example of this is the third constructor of string: string(const char *);
The implicit type conversion of const char * to a string object enables its users to write the following: string s; s = "Hello"; The compiler implicitly transforms this into string s; //pseudo C++ code: s = string ("Hello"); //create a temporary and assign it to s On the other hand, if you declare this constructor explicit, you have to use explicit type conversion: class string { //... public: explicit string(const char *); }; int main() { string s; s = string("Hello"); //explicit conversion now required return 0; } Extensive amounts of legacy C++ code rely on the implicit conversion of constructors. The C++ Standardization committee was aware of that. In order to not make existing code break, the implicit conversion was retained. However, a new keyword, explicit, was introduced to the languageto enable the programmer to block the implicit conversion when it is undesirable. As a rule, a constructor that can be invoked with a single argument needs to be declared explicit. When the implicit type conversion is intentional and well behaved, the constructor can be used as an implicit conversion operator.
就是這樣一個結構:
template <typename T>
struct Type2Type
  {
typedef T OriginalType;
};假定有個片斷如下,創建一個T*
template <class T,class U>
T* Create(const U& arg)
  {
return new T(arg);
}如果對于某個類如“Widget”,其ctor要有兩個參數,比如第二個參數必須是-1(對于舊的代碼來說,誰知道呢:)),但又不想另外創建一個“CreateWidget”方法,那么怎么辦呢,函數是不能偏特化的,即如下代碼:
在 VC7下會報告:非法使用顯式模板參數 只能使用函數重載,比如:
template <class T,class U>
 T* Create(const U&arg,T /**//*虛擬*/)
  {
return new T(arg);
}

template <class U>
 Widget * Create(const U& arg, Widget /**//*虛擬*/)
  {
return new Widget(arg,-1);
}
這樣是可以解決問題,但最大的毛病在于運行時構造了 未被使用的對象這個開銷(虛擬的Widget參數)。這時 Type2Type 這個咚咚出場了,按照書的說法,這是“一個型別代表物、一個可以讓你傳給重載函數的輕量級ID”,如下:
template <class T,class U>
T* Create(const U& arg,Type2Type<T>)
  {
return new T(arg);
}

template <class U>
Widget * Create(const U& arg,Type2Type<Widget>)
  {
return new Widget(arg,-1);
}

調用方:
String *pStr = Create("hello",Type2Type<String>());
Widget *pW = Create(100,Type2Type<Widget>());
關鍵是,這個東西也是給編譯器看的,妙
inline unsigned __int64 GetCycleCount(void)
  {
_asm _emit 0x0F
_asm _emit 0x31
}dasm如下:
: inline unsigned __int64 GetCycleCount(void)
 7: {
00401070 55 push ebp
00401071 8B EC mov ebp,esp
00401073 83 EC 40 sub esp,40h
00401076 53 push ebx
00401077 56 push esi
00401078 57 push edi
00401079 8D 7D C0 lea edi,[ebp-40h]
0040107C B9 10 00 00 00 mov ecx,10h
00401081 B8 CC CC CC CC mov eax,0CCCCCCCCh
00401086 F3 AB rep stos dword ptr [edi]
8: _asm _emit 0x0F
00401088 0F 31 rdtsc
10: }
0040108A 5F pop edi
0040108B 5E pop esi
0040108C 5B pop ebx
0040108D 83 C4 40 add esp,40h
00401090 3B EC cmp ebp,esp
00401092 E8 19 00 00 00 call __chkesp (004010b0)
00401097 8B E5 mov esp,ebp
00401099 5D pop ebp
0040109A C3 ret
 關鍵就是那個RDTSC指令,即 Read Time Stamp Counter, 結果會保存在EDX:EAX寄存器對中。 Intel的文檔是這樣說的:
With the Pentium processor, Intel added an additional instruction called RDTSC (Read Time Stamp
Counter). This gives software direct access to the number of clock counts the processor has
experienced since its last power-on or hardware reset. With contemporary clock rates of, for
example, 3.06 Ghz, that results in a timing period of only 0.326 nanoseconds.
|