青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

MyMSDN

MyMSDN記錄開發新知道

#

Expert C Programming notes

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

const_is_not_constant

2、早期的gets()中的Bug導致了Internet蠕蟲(P42)

gets()函數并不檢查緩沖區的空間,事實上它也無法檢查緩沖區的空間。如果函數的調用者提供了一個指向堆棧的指針,并且gets()函數讀入的字符數量超過緩沖區的空間,gets()函數將會愉快地將多出來的字符繼續寫入到堆棧中,這就覆蓋了堆棧原先的內容。——這就是病毒利用它來寫入額外空間,并引發蠕蟲病毒的前提。

推薦的方式是將

gets(line)

替換為

if(fgets(line, sizeof(line), stdin) == NULL)

exit(1);

3、相鄰字符串常量自動連接(P45)

這個其實已經應用很普遍了,但是我個人用的比較少,特此記錄一下。

ansi-c_connect_string

4、返回一個指針?(P48)

這個話題圍繞一個程序的BUG來展開,這個程序返回了局部變量的值的指針,這么說當然你一眼就能看得出來問題所在,但是在很多時候,這個錯誤卻總是在你的眼皮子底下溜走。

作者提供了五種方式,只能說可以用,但唯一推薦的只有一個,詳見作者的分析(P48)(不是什么高深的理論,你自己也能分析地出來)。

a.返回一個字符串常量的指針。因為常量存在靜態數據存儲區,所以指針沒問題。

b.使用全局聲明的數組。提到全局兩個字,就知道這個方法有很大的局限性。

c.使用靜態數組。下一次調用將覆蓋這個數組內容。

char * func() {

static char buffer[20];

return buffer;

}

d.顯式分配一些內存,保存返回的值。

char  * func() {

char * s = malloc(120);

return s;

}

既然用到了malloc,就必然伴隨著free,因此帶來了內存管理的問題,增加了開發者負擔。

e.(推薦)在調用前后,由函數調用者分配內存,并由其釋放,在同一地方釋放對于內存管理來說代價相對最小。

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);

posted @ 2009-04-01 00:31 volnet 閱讀(341) | 評論 (0)編輯 收藏

QuickSort快速排序法(2009-03-06)

本文代碼已經更新,修正嚴重BUG,最新版本詳見《QuickSort快速排序法(2009-10-28)》!

本文中所涉及的代碼未做更新,請移步最新版查閱正確代碼!

鏈接:http://www.shnenglu.com/mymsdn/archive/2009/10/28/quicksort20091028.html


快速排序法:(好土,感覺滿世界都會,不過還是寫一下,當然了,標準庫里多的是排序算法),這里還是實現經典版的快速排序了,時間復雜度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;
}

posted @ 2009-03-06 03:03 volnet 閱讀(1228) | 評論 (5)編輯 收藏

最大公約數問題

image

image

image

image

image 

image

image

image

image

以上內容摘自《編程之美》P150-154。

為了方便使用,下面是可拷貝的代碼:

Math.h

#pragma once

class Math
{
public:
    Math(void);
    ~Math(void);

public :
    //編程之美P150-154

    //求最大公約數,歐幾里德——輾轉相除法
    static int Gcd1(int x, int y);

    //求最大公約數,歐幾里德——輾轉相除法(變相將除法變成了減法)
    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
    // 也就是說迭代次數較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不支持負數,也就是最后兩行測試代碼無法通過。但是限于對負數的最大公約數并沒有定義,也就是說即便上面的Gcd1和Gcd2好像算出了負數,但它們的結果沒有意義。

posted @ 2009-03-04 23:52 volnet 閱讀(1050) | 評論 (0)編輯 收藏

a Swap program

#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看似繚亂卻是一個交換函數:

從最后一次做*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-的數值計算(基本上只能用在整數上,因為對浮點的操作可能涉及到舍入的問題)

而且這個方法還有一個缺陷,就是對數值邊界的判斷,比入MAX_INT+MAX_INT就溢出了,所以它存在一定的局限性。

posted @ 2009-02-24 18:29 volnet 閱讀(291) | 評論 (0)編輯 收藏

關于C++ Template分離頭文件和定義所產生的錯誤

編譯錯誤

如圖所示的代碼出現了如圖所示的錯誤,誰能解釋一下是為什么呢?

雖然在最后include進了cpp文件,而且這種做法也在C++ Primer中也是正確的(難道是標準和現實的差距?)。將代碼稍微變動,并將cpp部分的內容移到.h文件中的include位置即可正確編譯。

編譯正確

posted @ 2009-02-22 06:05 volnet 閱讀(4359) | 評論 (20)編輯 收藏

將數組作為實參傳遞

在C++中我們應該少用指針,多用引用,原因請大家自行搜索。在傳遞數組的時候我們需要格外注意,先讓我們看一個簡單的范例。

// 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;
}

首先這個范例無法編譯通過:

error C2782

原因就出在類型推斷上。根據定義,Func2的類型必須是T&,也就是說傳遞實參的時候,兩個形參必須是相同的,而這一點在模板編程中就會由編譯器來負責推斷。

Func1:

調用Func1(a, b)則推斷的類型分別是Func1(int*, int*),調用函數將會自動將數組的首地址指針作為實參進行傳遞,因此類型推斷兩形參相同,編譯通過!

Func2:

調用Func2(a, b)因為我們希望按引用的方式進行實參傳遞,因此需要遵循這樣的規律:

(P208)如果形參是數組的引用,編譯器將不會將數組實參轉化為指針,而是傳遞數組引用的本身。在這種情況下,數組大小成為形參和實參類型的一部分。

所以推斷類型分別是Func2(int (&)[10], int (&)[12]),因為int (&)[10] != int (&)[12],所以與T == T相悖!自然也就編譯不過了!

Func3:

該函數是Func2的一個靜態表示,通過上面的解釋應該很容易理解這個代碼了。

posted @ 2009-02-19 21:35 volnet 閱讀(1737) | 評論 (0)編輯 收藏

計時器代碼片段

這不是一個面向對象的代碼庫,它的存在僅僅只是為了說明幾個函數調用,如果要在您的工程中應用相關內容,請自行構建(這應該不難),或者看看我推薦的文檔。

// 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文檔,可以給我留言,我會將它發給你,在CSDN的下載中,您可能需要注冊成為相關網站的會員,或者使用積分,如果您覺得麻煩,可以直接向我索取!點此獲取!

posted @ 2009-02-19 01:49 volnet 閱讀(666) | 評論 (0)編輯 收藏

什么是句柄?為什么會有句柄?HANDLE

關鍵字:句柄, HANDLE, WINDOWS SDK, windows.h,

從廣義上,能夠從一個數值拎起一大堆數據的東西都可以叫做句柄。句柄的英文是"Handle",本義就是"柄",只是在計算機科學中,被特別地翻譯成"句柄",其實還是個"柄"。從一個小東西拎起一大堆東西,這難道不像是個"柄"嗎?

然后,指針其實也是一種"句柄",只是由于指針同時擁有更特殊的含義——實實在在地對應內存里地一個地址——所以,通常不把指針說成是"句柄"。但指針也有著能從一個32位的值引用到一大堆數據的作用,這不是句柄又是什么?

Windows系統中有許多內核對象(這里的對象不完全等價于"面向對象程序設計"一詞中的"對象",雖然實質上還真差不多),比如打開的文件,創建的線程,程序的窗口,等等。這些重要的對象肯定不是4個字節或者8個字節足以完全描述的,他們擁有大量的屬性。為了保存這樣一個"對象"的狀態,往往需要上百甚至上千字節的內存空間,那么怎么在程序間或程序內部的子過程(函數)之間傳遞這些數據呢?拖著這成百上千的字節拷貝來拷貝去嗎?顯然會浪費效率。那么怎么辦?當然傳遞這些對象的首地址是一個辦法,但這至少有兩個缺點:

  1. 暴露了內核對象本身,使得程序(而不是操作系統內核)也可以任意地修改對象地內部狀態(首地址都知道了,還有什么不能改的?),這顯然是操作系統內核所不允許的;
  2. 操作系統有定期整理內存的責任,如果一些內存整理過一次后,對象被搬走了怎么辦?

所以,Windows操作系統就采用進一步的間接:在進程的地址空間中設一張表,表里頭專門保存一些編號和由這個編號對應一個地址,而由那個地址去引用實際的對象,這個編號跟那個地址在數值上沒有任何規律性的聯系,純粹是個映射而已。

在Windows系統中,這個編號就叫做"句柄"。

?

Handle在Windows中的含義很廣泛,以下關于談到的Handle除非特別說明,將僅限于進程、線程的上下文中。

1、先來談談Handle

Handle本身是一個32位的無符號整數,它用來代表一個內核對象。它并不指向實際的內核對象,用戶模式下的程序永遠不可能獲得一個內核對象的實際地址(一般情況下)。那么Handle的意義何在?它實際上是作為一個索引在一個表中查找對應的內核對象的實際地址。那么這個表在哪里呢?每個進程都有這樣的一個表,叫句柄表。該表的第一項就是進程自己的句柄,這也是為什么你調用GetCurrentProcess()總是返回0x7FFFFFFF原因。

簡單地說,Handle就是一種用來"間接"代表一個內核對象的整數值。你可以在程序中使用handle來代表你想要操作的內核對象。這里的內核對象包括:事件(Event)、線程、進程、Mutex等等。我們最常見的就是文件句柄(file handle)。

另外要注意的是,Handle僅在其所屬的進程中才有意義。將一個進程擁有的handle傳給另一個進程沒有任何意義,如果非要這么做,則需要使用DuplicateHandle(),在多個進程間傳遞Handle是另外一個話題了,與這里要討論的無關。

2、進程ID

首先,進程ID是一個32位無符號整數,每個進程都有這樣的一個ID,并且該ID在系統范圍內是唯一的。系統使用該ID來唯一確定一個進程。

深入些說,系統可能使用進程ID來計算代表該進程的內核對象的基地址(及EPROCESS結構的基地址),具體的計算公式你可以去問微軟的OS開發人員。

3、HINSTANCE

HINSTANCE也是一個32無符號整數,它表示程序加載到內存中的基地址。

posted @ 2009-02-19 00:10 volnet 閱讀(25766) | 評論 (29)編輯 收藏

C++ notes (6)

51、static成員函數

因為static成員不是任何對象的組成部分,所以static成員函數不能被聲明為const。畢竟,將成員函數聲明為const就是承諾不會修改該函數所屬的對象。最后,static成員函數也不能被聲明為虛函數。

52、特殊的整型const static成員(P401)

const static數據成員在類的定義體中初始化時,該數據成員仍必須在類的定義體之外進行定義。

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)

下面是一些指導原則,有助于決定將操作符設置為類成員還是普通非成員函數

  • 賦值(=)、下標([])、調用(())和成員訪問箭頭(->)等操作符必須定義為成員,將這些操作符定義為非成員函數將在編譯時標記為錯誤。
  • 像賦值一樣,復合賦值操作符通常應定義為類的成員。與賦值不同的是,不一定非得這樣做,如果定義非成員復合賦值操作符,不會出現編譯錯誤。
  • 改變對象狀態或與給定類型緊密聯系的其他一些操作符,如自增、自減和解引用,通常應定義為類成員。
  • 對稱的操作符,如算術操作符、相等操作符、關系操作符和位操作符,最好定義為普通非成員函數。

54、區別操作符的前綴和后綴形式(P447)

同時定義前綴式操作符和后綴式操作符存在一個問題:它們的形參數目和類型相同,普通重載不能區別所定義的是前綴式操作符還是后綴式操作符。

為解決這一問題,后綴式操作符函數接受一個額外的(即,無用的)int型形參。使用后綴操作符時,編譯器提供0作為這個形參的實參。盡管我們的前綴式操作符函數可以使用這個額外的形參,但通常不應該這樣做。那個形參不是后綴式操作符的正常工作所需要的,它的唯一目的是使后綴函數與前綴函數區別開來。

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、函數對象(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是一個對象而不是函數,我們仍然可以“調用”該對象,效果是運行由absObj對象定義的重載調用操作符,該操作符接受一個int值并返回它的絕對值。

函數對象經常用作通用算法的實參。(詳見P450)

57、函數對象的函數適配器(P453)

標準庫提供了一組函數適配器(function adapter),用于特化和擴展一元和二元函數對象。函數適配器分為如下兩類:

(1)綁定器(binder),是一種函數適配器,它通過將一個操作數綁定到給定值而將二元函數對象轉換為一元函數對象。(bind1stbind2nd 更多

(2)求反器(negator),是一種函數適配器,它將謂詞函數對象的真值求反。(not1not2 更多

58、轉換操作符(P455)

轉換為什么有用?(詳見P454)

轉換函數采用如下通用形式:

operator type();

這里,type表示內置類型名、類類型名或由類型別名所定義的名字。對任何可作為函數返回類型的類型(除了void之外)都可以定義轉換函數。一般而言,不允許轉換為數組或函數類型,轉換為指針(數據或函數指針)以及引用類型是可以的。

轉換函數必須是成員函數,不能指定返回類型,并且形參表必須為空。

轉換函數一般不應該改變被轉換的對象。因此,轉換操作符通常應定義為const成員。

59、只能應用一個類類型轉換

類類型轉換之后不能再跟另一個類類型轉換。如果需要多個類類型轉換,則代碼將出錯。

假設有Integral=>SmallInt=>int,但是如果有一個函數cal(int),那么對于SmallInt si,可以使用cal(si),但對于Integral intVal;則不能使用cal(intVal)。語言只允許一次類類型轉換,所以該調用出錯。

60、virtual與其他成員函數(P479)

C++中的函數調用默認不使用動態綁定。要觸發動態綁定,必須滿足兩個條件:第一,只有指定為虛函數的成員函數才能進行動態綁定,成員函數默認為非虛函數,非虛函數不進行動態綁定;第二,必須通過基類類型的引用或指針進行函數調用。

基類類型引用和指針的關鍵點在于靜態類型(static type,在編譯時可知的引用類型或指針類型)和動態類型(dynamic type,指針或引用所綁定的對象的類型,這是僅在運行時可知的)可能不同。

posted @ 2009-02-15 02:28 volnet 閱讀(532) | 評論 (0)編輯 收藏

C++ notes (5)

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的常用似乎有了更充分的理由。

這里使用了一個簡單的時間函數大致如下:

#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)

每種實現都要求遵循以下原則:確保push_back操作高效地在vector中添加元素。從技術上來說,在原來為空的vector容器上n次調用push_back函數,從而創建擁有n個元素的vector容器,其執行時間永遠不能超過n的常量倍。

43、類定義中為何不能具有自身的數據成員(P375)

因為只有當類定義體完成后才能定義類,因此類不能具有自身類型的數據成員。然而,只要類名一出現就可以認為該類已聲明。因此,類的數據成員可以是指向自身類型的指針或引用:

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、形參表和函數體處于類作用域中,函數返回類型不一定在類作用域中

在定義于類外部的成員函數中,形參表和成員函數體都出現在成員名之后。這些都是在類作用域中定義,所以可以不用限定而引用其他成員。因為形參表是在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、構造函數初始化式(P387)

與任意的成員函數一樣,構造函數可以定義在類的內部或外部。構造函數初始化式只在構造函數的定義中而不是聲明中指定。

構造函數初始化列表難以理解的一個原因在于,省略初始化列表并在構造函數的函數體內對數據成員賦值是合法的。

在構造函數初始化列表中沒有顯式提及的每個成員,使用與初始化變量相同的規則來進行初始化。運行該類型的默認構造函數,來初始化類類型的數據成員。內置或復合類型的成員的初始值依賴于對象的作用域:在局部作用域中這些成員不被初始化,而在全局作用域中它們被初始化為0。

如果那個類沒有默認構造函數,則編譯器嘗試使用默認構造函數將會失敗。在這種情況下,為了初始化數據成員,必須提供初始化式。

對于這樣的成員,在構造函數函數體中對它們賦值不起作用。沒有默認構造函數的類類型成員,以及const或引用類型的成員,不管是哪種類型,都必須在構造函數初始化列表中進行初始化。

因為內置類型的成員不進行隱式初始化,所以對這些成員是進行初始化還是賦值似乎都無關緊要。除了兩個例外,對非類類型的數據成員進行賦值或使用初始化式在結果和性能上都是等價的。

48、成員初始化的次序

構造函數初始化列表僅指定用于初始化成員的值,并不指定這些初始化執行的次序。成員被初始化的次序就是定義成員的次序。

class X{

int i;

int j;

public:

//run-time error: i is initialized before j

X(int val): j(val), i(j) {}

}

在這種情況下,構造函數初始化列表看起來似乎是用val初始化j,然后再用j來初始化i。然而i首先被初始化。這個初始化列表的效果是用尚未初始化的j值來初始化i!

49、使用默認構造函數(P393)

常犯的一個錯誤是采用以下方式聲明一個用默認構造函數初始化的對象:

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為形參的構造函數,因此在調用需要MyClass對象的same_object成員函數時,會自動隱式調用該構造函數構建MyClass對象,用于操作。但生成的MyClass對象是臨時對象,在same_object函數調用完成后銷毀。如果為了避免產生隱式轉換可以使用explicit關鍵字來抑制由構造函數定義的隱式轉換:

explicit

posted @ 2009-02-12 01:06 volnet 閱讀(1250) | 評論 (0)編輯 收藏

僅列出標題
共9頁: 1 2 3 4 5 6 7 8 9 
特殊功能
 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            激情视频亚洲| 国内成人精品一区| 亚洲性视频h| 亚洲蜜桃精久久久久久久| 另类天堂av| 亚洲精品美女在线观看| 亚洲国产日韩综合一区| 美女亚洲精品| 在线视频欧美日韩| 亚洲视频久久| 国产午夜精品久久| 美女精品一区| 嫩草影视亚洲| 亚洲视频精选在线| 午夜精品短视频| 激情欧美一区二区三区| 亚洲第一主播视频| 欧美日韩亚洲在线| 久久久久久欧美| 欧美岛国激情| 香蕉尹人综合在线观看| 久久久久免费视频| 亚洲视频自拍偷拍| 欧美在线观看网址综合| 亚洲精品乱码视频| 亚洲一区二区四区| 亚洲国语精品自产拍在线观看| 亚洲免费观看高清完整版在线观看| 国产精品欧美激情| 裸体一区二区| 欧美性事免费在线观看| 久久一区二区三区超碰国产精品| 欧美激情偷拍| 久久九九国产| 欧美精品在线免费| 久久久久国产成人精品亚洲午夜| 欧美精品一区二区视频| 久久久一区二区三区| 欧美精品激情blacked18| 久久精品一区二区三区四区 | 欧美一级艳片视频免费观看| 久久永久免费| 欧美亚洲一区二区在线观看| 美女爽到呻吟久久久久| 欧美一级理论性理论a| 欧美成人午夜| 久久在线视频| 国产精品一区二区三区乱码 | 99精品视频免费| 久久激情婷婷| 欧美一区二区三区精品电影| 欧美日本免费一区二区三区| 麻豆精品在线视频| 国产精品久久久久久五月尺| 欧美电影免费观看高清| 国产女主播一区| 一区二区三区波多野结衣在线观看| 亚洲电影在线播放| 久久国产黑丝| 亚洲欧美日韩直播| 欧美日韩国产在线观看| 亚洲高清在线播放| 91久久精品国产| 久久婷婷影院| 蜜臀a∨国产成人精品| 国产一区二区三区黄| 亚洲一区国产一区| 亚洲免费一在线| 国产精品高潮视频| 亚洲天堂偷拍| 欧美在线亚洲一区| 国产日韩欧美中文| 久久爱另类一区二区小说| 午夜精品视频网站| 国产日韩欧美综合精品| 亚洲欧美日本国产有色| 欧美一乱一性一交一视频| 国产精品国产精品| 亚洲欧美电影院| 欧美在线视频免费| 国产一区二区无遮挡| 久久成人免费日本黄色| 久久久亚洲高清| 国内精品久久久久久久97牛牛| 欧美亚洲综合另类| 美日韩精品视频| 亚洲福利视频二区| 欧美成人一区二区三区片免费| 亚洲激情另类| 亚洲视频福利| 国产亚洲观看| 美脚丝袜一区二区三区在线观看| 欧美激情性爽国产精品17p| 一区二区三区不卡视频在线观看 | 一区二区三区福利| 久久成人国产| 亚洲国产女人aaa毛片在线| 欧美高清在线观看| 亚洲午夜av在线| 麻豆国产精品va在线观看不卡| 亚洲日本理论电影| 国产精品日韩一区二区| 久久久噜噜噜| 99精品热视频| 久久免费的精品国产v∧| 亚洲黄色成人久久久| 欧美视频网址| 久久成人一区| 一区二区三区视频观看| 久热这里只精品99re8久| 一区二区三区成人| 狠色狠色综合久久| 欧美午夜精品电影| 久久亚洲免费| 亚洲欧美日韩在线不卡| 91久久精品国产| 久久综合激情| 亚洲永久免费| 亚洲精品美女在线观看播放| 国产欧美日韩三级| 欧美freesex交免费视频| 亚洲综合不卡| 亚洲精品国产精品乱码不99 | 精品成人在线| 国产伦精品一区二区三区四区免费 | 欧美亚洲综合网| 亚洲国产三级在线| 国产欧美日韩在线播放| 欧美日韩国产三级| 欧美高清视频在线| 久久激情网站| 一区二区三区精品国产| 亚洲国产精品免费| 久久性色av| 久久精品一二三| 亚洲综合精品| 一区二区高清视频在线观看| 亚洲福利精品| 黄色精品一区| 国产三区精品| 国产精品美女www爽爽爽视频| 欧美另类变人与禽xxxxx| 久热这里只精品99re8久| 久久激情视频| 久久精品一区二区| 欧美一级专区| 久久精品国产一区二区电影| 亚洲一区在线观看免费观看电影高清| 亚洲高清自拍| 欧美成人中文| 欧美激情bt| 亚洲国产精品成人一区二区 | 亚洲精品在线免费| 日韩亚洲国产精品| 亚洲人成久久| 99在线精品观看| 一本久道久久综合中文字幕| 亚洲精品美女久久久久| 亚洲激情图片小说视频| 亚洲国产精品传媒在线观看| 伊人春色精品| 亚洲日本va午夜在线影院| 亚洲毛片一区二区| 一区二区三区国产精华| 亚洲午夜性刺激影院| 亚洲欧美成人综合| 欧美中文在线字幕| 久久天天躁夜夜躁狠狠躁2022| 久久久www成人免费无遮挡大片| 久久中文字幕一区二区三区| 欧美成人精品高清在线播放| 亚洲国产欧美不卡在线观看| av成人免费在线| 午夜一区二区三区不卡视频| 久久嫩草精品久久久久| 裸体女人亚洲精品一区| 欧美看片网站| 国产精品激情电影| 国产亚洲日本欧美韩国| 亚洲日本欧美在线| 午夜国产精品影院在线观看| 久久综合九色99| 亚洲区一区二区三区| 亚洲欧美另类在线观看| 免费毛片一区二区三区久久久| 欧美日韩你懂的| 国产一区久久| 亚洲午夜激情| 免费久久久一本精品久久区| 亚洲精品少妇| 久久久久99精品国产片| 欧美精品在线观看一区二区| 国产性天天综合网| 一区二区国产精品| 久久久综合精品| 中文高清一区| 欧美成人精品| 激情久久综艺| 久久成人国产| 在线视频日本亚洲性|