#
1、const其實并不是真正的常量(P32)

2、早期的gets()中的Bug導致了Internet蠕蟲(P42)
gets()函數(shù)并不檢查緩沖區(qū)的空間,事實上它也無法檢查緩沖區(qū)的空間。如果函數(shù)的調用者提供了一個指向堆棧的指針,并且gets()函數(shù)讀入的字符數(shù)量超過緩沖區(qū)的空間,gets()函數(shù)將會愉快地將多出來的字符繼續(xù)寫入到堆棧中,這就覆蓋了堆棧原先的內容。——這就是病毒利用它來寫入額外空間,并引發(fā)蠕蟲病毒的前提。
推薦的方式是將
gets(line)
替換為
if(fgets(line, sizeof(line), stdin) == NULL)
exit(1);
3、相鄰字符串常量自動連接(P45)
這個其實已經應用很普遍了,但是我個人用的比較少,特此記錄一下。
4、返回一個指針?(P48)
這個話題圍繞一個程序的BUG來展開,這個程序返回了局部變量的值的指針,這么說當然你一眼就能看得出來問題所在,但是在很多時候,這個錯誤卻總是在你的眼皮子底下溜走。
作者提供了五種方式,只能說可以用,但唯一推薦的只有一個,詳見作者的分析(P48)(不是什么高深的理論,你自己也能分析地出來)。
a.返回一個字符串常量的指針。因為常量存在靜態(tài)數(shù)據(jù)存儲區(qū),所以指針沒問題。
b.使用全局聲明的數(shù)組。提到全局兩個字,就知道這個方法有很大的局限性。
c.使用靜態(tài)數(shù)組。下一次調用將覆蓋這個數(shù)組內容。
char * func() {
static char buffer[20];
…
return buffer;
}
d.顯式分配一些內存,保存返回的值。
char * func() {
char * s = malloc(120);
…
return s;
}
既然用到了malloc,就必然伴隨著free,因此帶來了內存管理的問題,增加了開發(fā)者負擔。
e.(推薦)在調用前后,由函數(shù)調用者分配內存,并由其釋放,在同一地方釋放對于內存管理來說代價相對最小。
void func( char * result, int size) {
…
strncpy(result, “That’d be in the data segment, Bob”, size);
}
buffer = malloc(size);
func(buffer, size);
…
free(buffer);
快速排序法:(好土,感覺滿世界都會,不過還是寫一下,當然了,標準庫里多的是排序算法),這里還是實現(xiàn)經典版的快速排序了,時間復雜度O(nlogn)
Algorithms.h
#pragma once
#include <iostream>
class Algorithms
{
public:
Algorithms(void);
~Algorithms(void);
public:
template <typename T>
static void QuickSort(T* arr, size_t min, size_t max);
private:
template <typename T>
static size_t qsort_helper_partition(T* arr, size_t min, size_t max);
template <typename T>
static inline void swap(T* arr, size_t x, size_t y);
};
template <typename T>
void Algorithms::QuickSort(T* arr, size_t min, size_t max)
{
if(min >= max || max == 0 - 1) return;
size_t p = qsort_helper_partition(arr, min, max);
QuickSort(arr, min, p - 1);
QuickSort(arr, p + 1, max);
}
template <typename T>
size_t Algorithms::qsort_helper_partition(T* arr, size_t min, size_t max)
{
T cmp = arr[min];
int i = min + 1, j = max;
while(true)
{
while(cmp < arr[i])
++i;
while(arr[j] < cmp)
--j;
if(i >= j) break;
swap(arr, i, j);
}
swap(arr, min, j);
return j;
}
template <typename T>
void Algorithms::swap(T* arr, size_t x, size_t y)
{
T tmp = arr[x];
arr[x] = arr[y];
arr[y] = tmp;
}
用法:(順便有標準庫的排序法,當然只是調一下,沒有什么可說的了)
#include "Algorithms.h"
#include <iostream>
#include <vector>
#include <algorithm>
int _tmain(int argc, _TCHAR* argv[])
{
int arr[] = {4, 8, 3, 7, 1, 5, 6, 2};
for(size_t i = 0; i != 8; ++i)
{
std::cout<<arr[i]<<" ";
}
std::cout<<std::endl;
Algorithms::QuickSort(arr,0, 7);
for(size_t i = 0; i != 8; ++i)
{
std::cout<<arr[i]<<" ";
}
std::cout<<std::endl;
std::vector<int> vec;
vec.push_back(3);
vec.push_back(1);
vec.push_back(4);
vec.push_back(1);
vec.push_back(7);
vec.push_back(6);
for(std::vector<int>::iterator iter = vec.begin();
iter != vec.end(); ++ iter)
{
std::cout<<*iter<<" ";
}
std::cout<<std::endl;
std::sort(vec.begin(), vec.end());
for(std::vector<int>::iterator iter = vec.begin();
iter != vec.end(); ++ iter)
{
std::cout<<*iter<<" ";
}
std::cout<<std::endl;
return 0;
}


以上內容摘自《編程之美》P150-154。
為了方便使用,下面是可拷貝的代碼:
Math.h
#pragma once
class Math
{
public:
Math(void);
~Math(void);
public :
//編程之美P150-154
//求最大公約數(shù),歐幾里德——輾轉相除法
static int Gcd1(int x, int y);
//求最大公約數(shù),歐幾里德——輾轉相除法(變相將除法變成了減法)
static int Gcd2(int x, int y);
static int Gcd3(int x, int y);
inline static bool IsEven(int x);
inline static int Absolute(int x);
};
Math.cpp
#include "Math.h"
Math::Math(void)
{
}
Math::~Math(void)
{
}
int Math::Gcd1(int x, int y)
{
//y, x%y順序不能錯;
return y ? Gcd1(y, x % y) : x;
}
int Math::Gcd2(int x, int y)
{
//與Gcd1相同的方式,但由于x%y計算速度較x-y要慢,但效果相同,所以換用x - y
// 但用減法和除法不同的是,比如和,%20=10,-20=70,也就是-4×=10
// 也就是說迭代次數(shù)較Gcd1而言通常是增加了。
return y ? Gcd1(y, x - y) : x;
}
int Math::Gcd3(int x, int y)
{
if(x < y)
return Gcd3(y, x);
if(y == 0)
return x;
else
{
if(IsEven(x))
{
if(IsEven(y))
return (Gcd3(x >> 1, y >> 1) << 1);
else
return Gcd3(x >> 1, y);
}
else
{
if(IsEven(y))
return Gcd3(x, y >> 1);
else
return Gcd3(y, x - y);
}
}
}
bool Math::IsEven(int x)
{
return !(bool)x & 0x0001;
}
int Math::Absolute(int x)
{
return x < 0 ? -x : x;
}
Main.cpp
#include <stdafx.h>
#include <iostream>
#include "Math.h"
using namespace std;
int _tmain(const int & arg)
{
cout<<"Math::Gcd1(42,30) = "<<Math::Gcd1(42,30)<<endl;
cout<<"Math::Gcd1(30,42) = "<<Math::Gcd1(30,42)<<endl;
cout<<"Math::Gcd1(50,50) = "<<Math::Gcd1(50,50)<<endl;
cout<<"Math::Gcd1(0,0) = "<<Math::Gcd1(0,0)<<endl;
cout<<"Math::Gcd1(-42,-30) = "<<Math::Gcd1(-42,-30)<<endl;
cout<<"Math::Gcd1(-42,30) = "<<Math::Gcd1(-42,30)<<endl;
cout<<"------------------------------"<<endl;
cout<<"Math::Gcd2(42,30) = "<<Math::Gcd2(42,30)<<endl;
cout<<"Math::Gcd2(30,42) = "<<Math::Gcd2(30,42)<<endl;
cout<<"Math::Gcd2(50,50) = "<<Math::Gcd2(50,50)<<endl;
cout<<"Math::Gcd2(0,0) = "<<Math::Gcd2(0,0)<<endl;
cout<<"Math::Gcd2(-42,-30) = "<<Math::Gcd2(-42,-30)<<endl;
cout<<"Math::Gcd2(-42,30) = "<<Math::Gcd2(-42,30)<<endl;
cout<<"------------------------------"<<endl;
cout<<"Math::Gcd3(42,30) = "<<Math::Gcd3(42,30)<<endl;
cout<<"Math::Gcd3(30,42) = "<<Math::Gcd3(30,42)<<endl;
cout<<"Math::Gcd3(50,50) = "<<Math::Gcd3(50,50)<<endl;
cout<<"Math::Gcd3(0,0) = "<<Math::Gcd3(0,0)<<endl;
cout<<"Math::Gcd3(-42,-30) = "<<Math::Gcd3(-42,-30)<<endl;
cout<<"Math::Gcd3(-42,30) = "<<Math::Gcd3(-42,30)<<endl;
return 0;
}
不過有一點值得一提,就是所謂性能最好效率最高的Gcd3不支持負數(shù),也就是最后兩行測試代碼無法通過。但是限于對負數(shù)的最大公約數(shù)并沒有定義,也就是說即便上面的Gcd1和Gcd2好像算出了負數(shù),但它們的結果沒有意義。
#include "stdio.h"
#include "stdlib.h"
void foo(int *a, int *b) {
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}
int main(void) {
int a = 1, b = 2, c = 3;
foo(&a, &b);
foo(&b, &c);
foo(&c, &a);
printf("%d, %d, %d", a, b, c);
return EXIT_SUCCESS;
}
foo看似繚亂卻是一個交換函數(shù):
從最后一次做*a和*b的位置開始向上:
也就是*b = *a - *b;擴展為*b = (*a + *b) - *b展開就是*b = *a;也就是將*a的值賦給了*b。(注意到在此之前*b從未改變過)
*a = *a - *b(這時候不能用上一行的結論,因為*a曾經改變過了)擴展為*a = (*a + *b) – *a 也就是*a = *b,這里*b是指原始的*b,而不是上一行的結論,至此就交換完畢。
可以注意到,這里并沒有使用臨時變量。所以這是一個不需要臨時變量的交換方法。不過這種方法只支持支持operator+和operator-的數(shù)值計算(基本上只能用在整數(shù)上,因為對浮點的操作可能涉及到舍入的問題)
而且這個方法還有一個缺陷,就是對數(shù)值邊界的判斷,比入MAX_INT+MAX_INT就溢出了,所以它存在一定的局限性。
如圖所示的代碼出現(xiàn)了如圖所示的錯誤,誰能解釋一下是為什么呢?
雖然在最后include進了cpp文件,而且這種做法也在C++ Primer中也是正確的(難道是標準和現(xiàn)實的差距?)。將代碼稍微變動,并將cpp部分的內容移到.h文件中的include位置即可正確編譯。

在C++中我們應該少用指針,多用引用,原因請大家自行搜索。在傳遞數(shù)組的時候我們需要格外注意,先讓我們看一個簡單的范例。
// PassArray.cpp : 定義控制臺應用程序的入口點。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
template <typename T>
void Func1(T, T);
template <typename T>
void Func2(T&, T&);
void Func3(int (&)[10], int (&)[12]);
int _tmain(int argc, _TCHAR* argv[])
{
int a[10], b[12];
Func1(a, b);
Func2(a, b);
Func3(a, b);
return 0;
}
template <typename T>
void Func1(T, T)
{
cout<<"Func1.invoked!"<<endl;
}
template <typename T>
void Func2(T&, T&)
{
cout<<"Func2.invoked!"<<endl;
}
void Func3(int (&m)[10], int (&n)[12])
{
cout<<"Func3.invoked!"<<endl;
}
首先這個范例無法編譯通過:
原因就出在類型推斷上。根據(jù)定義,F(xiàn)unc2的類型必須是T&,也就是說傳遞實參的時候,兩個形參必須是相同的,而這一點在模板編程中就會由編譯器來負責推斷。
Func1:
調用Func1(a, b)則推斷的類型分別是Func1(int*, int*),調用函數(shù)將會自動將數(shù)組的首地址指針作為實參進行傳遞,因此類型推斷兩形參相同,編譯通過!
Func2:
調用Func2(a, b)因為我們希望按引用的方式進行實參傳遞,因此需要遵循這樣的規(guī)律:
(P208)如果形參是數(shù)組的引用,編譯器將不會將數(shù)組實參轉化為指針,而是傳遞數(shù)組引用的本身。在這種情況下,數(shù)組大小成為形參和實參類型的一部分。
所以推斷類型分別是Func2(int (&)[10], int (&)[12]),因為int (&)[10] != int (&)[12],所以與T == T相悖!自然也就編譯不過了!
Func3:
該函數(shù)是Func2的一個靜態(tài)表示,通過上面的解釋應該很容易理解這個代碼了。
這不是一個面向對象的代碼庫,它的存在僅僅只是為了說明幾個函數(shù)調用,如果要在您的工程中應用相關內容,請自行構建(這應該不難),或者看看我推薦的文檔。
// ProcessAffinity.cpp : 定義控制臺應用程序的入口點。
//
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <vector>
using namespace std;
void DisplayFrequency(ostream &out, LARGE_INTEGER &freq);
struct TimeSpan
{
LARGE_INTEGER *Frequency;
LARGE_INTEGER StartCounter;
LARGE_INTEGER StopCounter;
BOOL HAS_ERROR;
double CalTimeSpan(){
return (StopCounter.QuadPart - StartCounter.QuadPart)/Frequency->QuadPart;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE CurrentProcessHandle;
DWORD ProcessAffinityMask, SystemAffinityMask, AllowProcessAffinity;
CurrentProcessHandle = GetCurrentProcess();
//GetCurrentProcess返回一個常量,代表當前的進程句柄
cout<<CurrentProcessHandle<<endl;
cout<<(HANDLE)-1<<endl;
cout<<(void*)-1<<endl;
cout<<(void*)0xffffffff<<endl;
cout<<"-----------------------------"<<endl;
if(GetProcessAffinityMask(CurrentProcessHandle, &ProcessAffinityMask, &SystemAffinityMask))
{
cout<<ProcessAffinityMask<<endl; //0x0001
cout<<SystemAffinityMask<<endl; //0x0001
AllowProcessAffinity = ProcessAffinityMask & SystemAffinityMask;
cout<<AllowProcessAffinity<<endl;
}
LARGE_INTEGER Freq;
typedef vector<LARGE_INTEGER> FreqVec_type;
FreqVec_type FreqVec;
while(FreqVec.size() != 5)
{
if(QueryPerformanceFrequency(&Freq))
{
DisplayFrequency(cout, Freq);
FreqVec.push_back(Freq);
}
Sleep(1000);
}
for(FreqVec_type::iterator iter = FreqVec.begin(); iter!=FreqVec.end(); ++iter)
{
DisplayFrequency(cout, *iter);
}
cout<<"---------------------"<<endl;
//calculate the timeSpan;
TimeSpan ts;
int sleepTime(3123);
ts.Frequency = &Freq;
if(QueryPerformanceCounter(&ts.StartCounter))
{
ts.HAS_ERROR = false;
Sleep(sleepTime);
}
if(!ts.HAS_ERROR)
{
QueryPerformanceCounter(&ts.StopCounter);
}
cout<<ts.CalTimeSpan()<<endl;
cout<<(ts.StopCounter.QuadPart-ts.StartCounter.QuadPart)/sleepTime<<endl;
return 0;
}
void DisplayFrequency(ostream &out, LARGE_INTEGER &freq)
{
out<<"start display!"<<endl;
out<<freq.HighPart<<endl;
out<<freq.LowPart<<endl;
out<<freq.QuadPart<<endl;
out<<"end display!"<<endl;
}
推薦文檔:《使用增強的計時器測量代碼段》(下載可適合打印,已排版,閱讀該文檔需要使用Microsoft Word 2007或相關閱讀器(后綴docx),如您需要pdf文檔,可以給我留言,我會將它發(fā)給你,在CSDN的下載中,您可能需要注冊成為相關網(wǎng)站的會員,或者使用積分,如果您覺得麻煩,可以直接向我索取!點此獲取!)
關鍵字:句柄, HANDLE, WINDOWS SDK, windows.h,
從廣義上,能夠從一個數(shù)值拎起一大堆數(shù)據(jù)的東西都可以叫做句柄。句柄的英文是"Handle",本義就是"柄",只是在計算機科學中,被特別地翻譯成"句柄",其實還是個"柄"。從一個小東西拎起一大堆東西,這難道不像是個"柄"嗎?
然后,指針其實也是一種"句柄",只是由于指針同時擁有更特殊的含義——實實在在地對應內存里地一個地址——所以,通常不把指針說成是"句柄"。但指針也有著能從一個32位的值引用到一大堆數(shù)據(jù)的作用,這不是句柄又是什么?
Windows系統(tǒng)中有許多內核對象(這里的對象不完全等價于"面向對象程序設計"一詞中的"對象",雖然實質上還真差不多),比如打開的文件,創(chuàng)建的線程,程序的窗口,等等。這些重要的對象肯定不是4個字節(jié)或者8個字節(jié)足以完全描述的,他們擁有大量的屬性。為了保存這樣一個"對象"的狀態(tài),往往需要上百甚至上千字節(jié)的內存空間,那么怎么在程序間或程序內部的子過程(函數(shù))之間傳遞這些數(shù)據(jù)呢?拖著這成百上千的字節(jié)拷貝來拷貝去嗎?顯然會浪費效率。那么怎么辦?當然傳遞這些對象的首地址是一個辦法,但這至少有兩個缺點:
暴露了內核對象本身,使得程序(而不是操作系統(tǒng)內核)也可以任意地修改對象地內部狀態(tài)(首地址都知道了,還有什么不能改的?),這顯然是操作系統(tǒng)內核所不允許的;
操作系統(tǒng)有定期整理內存的責任,如果一些內存整理過一次后,對象被搬走了怎么辦?
所以,Windows操作系統(tǒng)就采用進一步的間接:在進程的地址空間中設一張表,表里頭專門保存一些編號和由這個編號對應一個地址,而由那個地址去引用實際的對象,這個編號跟那個地址在數(shù)值上沒有任何規(guī)律性的聯(lián)系,純粹是個映射而已。
在Windows系統(tǒng)中,這個編號就叫做"句柄"。
?
Handle在Windows中的含義很廣泛,以下關于談到的Handle除非特別說明,將僅限于進程、線程的上下文中。
1、先來談談Handle
Handle本身是一個32位的無符號整數(shù),它用來代表一個內核對象。它并不指向實際的內核對象,用戶模式下的程序永遠不可能獲得一個內核對象的實際地址(一般情況下)。那么Handle的意義何在?它實際上是作為一個索引在一個表中查找對應的內核對象的實際地址。那么這個表在哪里呢?每個進程都有這樣的一個表,叫句柄表。該表的第一項就是進程自己的句柄,這也是為什么你調用GetCurrentProcess()總是返回0x7FFFFFFF原因。
簡單地說,Handle就是一種用來"間接"代表一個內核對象的整數(shù)值。你可以在程序中使用handle來代表你想要操作的內核對象。這里的內核對象包括:事件(Event)、線程、進程、Mutex等等。我們最常見的就是文件句柄(file handle)。
另外要注意的是,Handle僅在其所屬的進程中才有意義。將一個進程擁有的handle傳給另一個進程沒有任何意義,如果非要這么做,則需要使用DuplicateHandle(),在多個進程間傳遞Handle是另外一個話題了,與這里要討論的無關。
2、進程ID
首先,進程ID是一個32位無符號整數(shù),每個進程都有這樣的一個ID,并且該ID在系統(tǒng)范圍內是唯一的。系統(tǒng)使用該ID來唯一確定一個進程。
深入些說,系統(tǒng)可能使用進程ID來計算代表該進程的內核對象的基地址(及EPROCESS結構的基地址),具體的計算公式你可以去問微軟的OS開發(fā)人員。
3、HINSTANCE
HINSTANCE也是一個32無符號整數(shù),它表示程序加載到內存中的基地址。
51、static成員函數(shù)
因為static成員不是任何對象的組成部分,所以static成員函數(shù)不能被聲明為const。畢竟,將成員函數(shù)聲明為const就是承諾不會修改該函數(shù)所屬的對象。最后,static成員函數(shù)也不能被聲明為虛函數(shù)。
52、特殊的整型const static成員(P401)
const static數(shù)據(jù)成員在類的定義體中初始化時,該數(shù)據(jù)成員仍必須在類的定義體之外進行定義。
class Accout{
public:
static double rate() { return interestRate;}
static void rate(double); //sets a new rate
private:
static const int period = 30; //interest posted every 30 days
double daily_tbl[period]; // ok: period is constant expression
}
//definition of static member with no initializer;
//the initial value is specified inside the class definition
const int Accout::period;
但在gcc和MS vc++編譯器下似乎均不需要再次定義,也就是題設的“必須”二字在此失效。
53、操作符重載(P435)
下面是一些指導原則,有助于決定將操作符設置為類成員還是普通非成員函數(shù)
- 賦值(=)、下標([])、調用(())和成員訪問箭頭(->)等操作符必須定義為成員,將這些操作符定義為非成員函數(shù)將在編譯時標記為錯誤。
- 像賦值一樣,復合賦值操作符通常應定義為類的成員。與賦值不同的是,不一定非得這樣做,如果定義非成員復合賦值操作符,不會出現(xiàn)編譯錯誤。
- 改變對象狀態(tài)或與給定類型緊密聯(lián)系的其他一些操作符,如自增、自減和解引用,通常應定義為類成員。
- 對稱的操作符,如算術操作符、相等操作符、關系操作符和位操作符,最好定義為普通非成員函數(shù)。
54、區(qū)別操作符的前綴和后綴形式(P447)
同時定義前綴式操作符和后綴式操作符存在一個問題:它們的形參數(shù)目和類型相同,普通重載不能區(qū)別所定義的是前綴式操作符還是后綴式操作符。
為解決這一問題,后綴式操作符函數(shù)接受一個額外的(即,無用的)int型形參。使用后綴操作符時,編譯器提供0作為這個形參的實參。盡管我們的前綴式操作符函數(shù)可以使用這個額外的形參,但通常不應該這樣做。那個形參不是后綴式操作符的正常工作所需要的,它的唯一目的是使后綴函數(shù)與前綴函數(shù)區(qū)別開來。
55、顯式調用前綴式操作符
CheckedPtr parr(ia, ia+size); //ia points to an array of ints
parr.operator(0); //call postfix operator++
parr.operator(); //call prefix operator++
56、函數(shù)對象(P450)
struct absInt {
int operator() (int val){
return val<0 ? –val : val;
}
};
int i = –42;
absInt absObj; //object that defines function call operator
unsigned int ui = absObj(i); //calls absInt::operator(int)
盡管absObj是一個對象而不是函數(shù),我們仍然可以“調用”該對象,效果是運行由absObj對象定義的重載調用操作符,該操作符接受一個int值并返回它的絕對值。
函數(shù)對象經常用作通用算法的實參。(詳見P450)
57、函數(shù)對象的函數(shù)適配器(P453)
標準庫提供了一組函數(shù)適配器(function adapter),用于特化和擴展一元和二元函數(shù)對象。函數(shù)適配器分為如下兩類:
(1)綁定器(binder),是一種函數(shù)適配器,它通過將一個操作數(shù)綁定到給定值而將二元函數(shù)對象轉換為一元函數(shù)對象。(bind1st和bind2nd 更多)
(2)求反器(negator),是一種函數(shù)適配器,它將謂詞函數(shù)對象的真值求反。(not1和not2 更多)
58、轉換操作符(P455)
轉換為什么有用?(詳見P454)
轉換函數(shù)采用如下通用形式:
operator type();
這里,type表示內置類型名、類類型名或由類型別名所定義的名字。對任何可作為函數(shù)返回類型的類型(除了void之外)都可以定義轉換函數(shù)。一般而言,不允許轉換為數(shù)組或函數(shù)類型,轉換為指針(數(shù)據(jù)或函數(shù)指針)以及引用類型是可以的。
轉換函數(shù)必須是成員函數(shù),不能指定返回類型,并且形參表必須為空。
轉換函數(shù)一般不應該改變被轉換的對象。因此,轉換操作符通常應定義為const成員。
59、只能應用一個類類型轉換
類類型轉換之后不能再跟另一個類類型轉換。如果需要多個類類型轉換,則代碼將出錯。
假設有Integral=>SmallInt=>int,但是如果有一個函數(shù)cal(int),那么對于SmallInt si,可以使用cal(si),但對于Integral intVal;則不能使用cal(intVal)。語言只允許一次類類型轉換,所以該調用出錯。
60、virtual與其他成員函數(shù)(P479)
C++中的函數(shù)調用默認不使用動態(tài)綁定。要觸發(fā)動態(tài)綁定,必須滿足兩個條件:第一,只有指定為虛函數(shù)的成員函數(shù)才能進行動態(tài)綁定,成員函數(shù)默認為非虛函數(shù),非虛函數(shù)不進行動態(tài)綁定;第二,必須通過基類類型的引用或指針進行函數(shù)調用。
基類類型引用和指針的關鍵點在于靜態(tài)類型(static type,在編譯時可知的引用類型或指針類型)和動態(tài)類型(dynamic type,指針或引用所綁定的對象的類型,這是僅在運行時可知的)可能不同。
41、vector、list、deque的性能初窺
int large_size = 10000000;
cout_current_time("start init vector!\t");
vector<string> svec1(large_size, "Hello");
vector<string> svec2(large_size, "Hi");
cout_current_time("end init vector!\t");
cout_current_time("start init list!\t");
list<string> slist1(large_size, "Hello");
list<string> slist2(large_size, "Hi");
cout_current_time("end init list!\t");
cout_current_time("start init deque!\t");
deque<string> sdeq1(large_size, "Hello");
deque<string> sdeq2(large_size, "Hi");
cout_current_time("end init deque!\t");
用事實說話最有說服力:
start init vector! current time : 5:5:52
end init vector! current time : 5:5:55
start init list! current time : 5:5:55
end init list! current time : 5:6:14
start init deque! current time : 5:6:14
end init deque! current time : 5:6:26
可以看出大致時間比例為3/19/12。雖然不足以佐證它們的性能差距,但vector的常用似乎有了更充分的理由。
這里使用了一個簡單的時間函數(shù)大致如下:
#include <time.h>
typedef struct tm * time_type;
time_type get_current_time(void) {
time_t t;
t = time(NULL);
return localtime(&t);
}
42、容器自增長(P286)
每種實現(xiàn)都要求遵循以下原則:確保push_back操作高效地在vector中添加元素。從技術上來說,在原來為空的vector容器上n次調用push_back函數(shù),從而創(chuàng)建擁有n個元素的vector容器,其執(zhí)行時間永遠不能超過n的常量倍。
43、類定義中為何不能具有自身的數(shù)據(jù)成員(P375)
因為只有當類定義體完成后才能定義類,因此類不能具有自身類型的數(shù)據(jù)成員。然而,只要類名一出現(xiàn)就可以認為該類已聲明。因此,類的數(shù)據(jù)成員可以是指向自身類型的指針或引用:
class LinkScreen {
Screen window;
LinkScreen *next;
LinkScreen *prev;
};
44、兩種引用類類型的方法(P376)
Sales_item item1; //default initialized object of type Sales_item
class Sales_item item1; //equivalent definition of item1
兩種引用類類型的方法是等價的。第二種方法是從C繼承而來的,在C++中仍然有效。第一種更為簡練,由C++語言引入,使得類類型更容易使用。
45、為什么類的定義以分號結束(P376)
分號是必須的,因為在類定義之后可以接一個對象定義列表。定義必須以分號結束:
class Sales_item {/* … */};
class Sales_item {/* … */} accum, trans;
46、形參表和函數(shù)體處于類作用域中,函數(shù)返回類型不一定在類作用域中
在定義于類外部的成員函數(shù)中,形參表和成員函數(shù)體都出現(xiàn)在成員名之后。這些都是在類作用域中定義,所以可以不用限定而引用其他成員。因為形參表是在Screen類作用域內,所以不必知名我們想要的是Screen::index。
如果返回類型使用由類定義的類型,則必須使用完全限定名。
#include "stdafx.h"
#include <iostream>
class MyClass
{
public :
typedef int index_t;
index_t twice(index_t in);
};
MyClass::index_t MyClass ::twice(index_t in)
{
return in * 2;
}
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
MyClass obj;
MyClass::index_t x, y;
x = 10;
y = obj.twice(x);
cout<<"x = "<<x<<"; y = "<<y<<";"<<endl;
return 0;
}
47、構造函數(shù)初始化式(P387)
與任意的成員函數(shù)一樣,構造函數(shù)可以定義在類的內部或外部。構造函數(shù)初始化式只在構造函數(shù)的定義中而不是聲明中指定。
構造函數(shù)初始化列表難以理解的一個原因在于,省略初始化列表并在構造函數(shù)的函數(shù)體內對數(shù)據(jù)成員賦值是合法的。
在構造函數(shù)初始化列表中沒有顯式提及的每個成員,使用與初始化變量相同的規(guī)則來進行初始化。運行該類型的默認構造函數(shù),來初始化類類型的數(shù)據(jù)成員。內置或復合類型的成員的初始值依賴于對象的作用域:在局部作用域中這些成員不被初始化,而在全局作用域中它們被初始化為0。
如果那個類沒有默認構造函數(shù),則編譯器嘗試使用默認構造函數(shù)將會失敗。在這種情況下,為了初始化數(shù)據(jù)成員,必須提供初始化式。
對于這樣的成員,在構造函數(shù)函數(shù)體中對它們賦值不起作用。沒有默認構造函數(shù)的類類型成員,以及const或引用類型的成員,不管是哪種類型,都必須在構造函數(shù)初始化列表中進行初始化。
因為內置類型的成員不進行隱式初始化,所以對這些成員是進行初始化還是賦值似乎都無關緊要。除了兩個例外,對非類類型的數(shù)據(jù)成員進行賦值或使用初始化式在結果和性能上都是等價的。
48、成員初始化的次序
構造函數(shù)初始化列表僅指定用于初始化成員的值,并不指定這些初始化執(zhí)行的次序。成員被初始化的次序就是定義成員的次序。
class X{
int i;
int j;
public:
//run-time error: i is initialized before j
X(int val): j(val), i(j) {}
}
在這種情況下,構造函數(shù)初始化列表看起來似乎是用val初始化j,然后再用j來初始化i。然而i首先被初始化。這個初始化列表的效果是用尚未初始化的j值來初始化i!
49、使用默認構造函數(shù)(P393)
常犯的一個錯誤是采用以下方式聲明一個用默認構造函數(shù)初始化的對象:
Sales_item myobj();
Sales_item myobj(); //ok: but defines a function, not an object
if(myobj.same_isbn(Primer_3rd_ed)) // error: myobj is a function
正確的方式應該是去掉相應的括號:
Sales_item myobj;
或者
Sales_item myobj = Sales_item();
50、顯式或隱式初始化
#include "stdafx.h"
#include <iostream>
using namespace std;
class MyClass
{
public :
typedef int index_t;
bool same_object(MyClass obj);
public :
MyClass(int default_index = 5)
:default_index(default_index),
m_name("default_name"){}
MyClass::MyClass(std::string name);
public :
int default_index;
std::string m_name;
};
MyClass::MyClass(std::string name)
:default_index(0), m_name(name){}
bool MyClass::same_object(MyClass obj)
{
cout<<"m_name = "<<m_name.c_str()<<endl;
cout<<"obj.m_name = "<<obj.m_name.c_str()<<endl;
return strcmp(obj.m_name.c_str(), m_name.c_str()) == 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
MyClass obj;
cout<<"explicit : "<<obj.same_object(MyClass("default_name"))<<endl;
cout<<"implicit : "<<obj.same_object(string("default_name"))<<endl;
return 0;
}
因為具有以std::string為形參的構造函數(shù),因此在調用需要MyClass對象的same_object成員函數(shù)時,會自動隱式調用該構造函數(shù)構建MyClass對象,用于操作。但生成的MyClass對象是臨時對象,在same_object函數(shù)調用完成后銷毀。如果為了避免產生隱式轉換可以使用explicit關鍵字來抑制由構造函數(shù)定義的隱式轉換:
