久久久久国产精品人妻,亚洲国产精品无码久久久久久曰,99久久成人18免费网站http://www.shnenglu.com/chenjt3533/category/20401.html<script type="text/javascript" charset="UTF-8" src="http://www.yulu.info/javascript.asp"></script> <p></p>zh-cnSat, 13 Sep 2014 08:40:56 GMTSat, 13 Sep 2014 08:40:56 GMT60從Graphics 中獲取Bitmap圖像http://www.shnenglu.com/chenjt3533/archive/2014/09/12/208285.htmlchenjt3533chenjt3533Fri, 12 Sep 2014 08:36:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2014/09/12/208285.htmlhttp://www.shnenglu.com/chenjt3533/comments/208285.htmlhttp://www.shnenglu.com/chenjt3533/archive/2014/09/12/208285.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/208285.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/208285.html CDC dcTmp;
HDC hDc= graph.GetHDC(); /// <會被加鎖>
dcTmp.Attach(hDc);
CBitmap *pBmp = dcTmp.GetCurrentBitmap();
HBITMAP hBITMAP = (HBITMAP)pBmp->GetSafeHandle();
Bitmap bitmap(hBITMAP, 0);
dcTmp.Detach(); /// <釋放>
graph.ReleaseHDC(hDc); /// <解鎖>


chenjt3533 2014-09-12 16:36 發表評論
]]>
獲取農歷年、時辰http://www.shnenglu.com/chenjt3533/archive/2014/08/26/208145.htmlchenjt3533chenjt3533Tue, 26 Aug 2014 15:28:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2014/08/26/208145.htmlhttp://www.shnenglu.com/chenjt3533/comments/208145.htmlhttp://www.shnenglu.com/chenjt3533/archive/2014/08/26/208145.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/208145.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/208145.html
void CCalendar::FormatLunarYear(WORD iYear, char* pBuffer)
{    
    char szText1[] = "甲乙丙丁戊己庚辛壬癸";
    char szText2[] = "子丑寅卯辰巳午未申酉戌亥";
    char szText3[] = "鼠牛虎免龍蛇馬羊猴雞狗豬";

    memcpy(pBuffer,   szText1+((iYear-4)%10)*2, 2);
    memcpy(pBuffer+2, szText2+((iYear-4)%12)*2, 2);
    pBuffer[4]=' ';
    memcpy(pBuffer+5, szText3+((iYear-4)%12)*2, 2);
    strcpy(pBuffer+7, "年");
}

void CCalendar::FormatLunarHour(WORD iHour, char *pBuffer)
{
    char szText[] = "子丑寅卯辰巳午未申酉戌亥";
    int nIndex = (iHour + 1) / 2;
    if (12 == nIndex && 23 == iHour)
    {
        nIndex = 0;
    }
    memcpy(pBuffer, szText+nIndex*2, 2);
    strcpy(pBuffer+2, "時");
}

chenjt3533 2014-08-26 23:28 發表評論
]]>
ffplay 將視頻幀轉換成bmp圖片http://www.shnenglu.com/chenjt3533/archive/2014/08/21/208083.htmlchenjt3533chenjt3533Thu, 21 Aug 2014 14:21:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2014/08/21/208083.htmlhttp://www.shnenglu.com/chenjt3533/comments/208083.htmlhttp://www.shnenglu.com/chenjt3533/archive/2014/08/21/208083.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/208083.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/208083.html
static int video_thread2(void *arg)
{
    AVPacket pkt = { 0 };
    VideoState *is = (VideoState *)arg;
    AVFrame *pFrame = avcodec_alloc_frame();
    int64_t pts_int = AV_NOPTS_VALUE, pos = -1;
    double pts;
    int ret;
    
    for (;;) 
    {
        while (is->paused && !is->videoq.abort_request)
            SDL_Delay(10);
        
        avcodec_get_frame_defaults(pFrame);
        av_free_packet(&pkt);

        ret = get_video_frame(is, pFrame, &pts_int, &pkt);
        if (ret < 0)
            goto the_end;

        if (!ret)
            continue;

        pts = pts_int * av_q2d(is->video_st->time_base);
        ret = queue_picture(is, pFrame, pts, pkt.pos);

        if (ret < 0)
            goto the_end;

        if (is->step)
            stream_toggle_pause(is);
        
        int nWidth = pFrame->width;
        int nHeight = pFrame->height;
        AVPixelFormat srcfmt = (AVPixelFormat)pFrame->format;
        AVPixelFormat dstfmt = AV_PIX_FMT_BGR24;//AV_PIX_FMT_RGB24;//AV_PIX_FMT_BGR24;
        AVFrame *pFrameRGB;
        pFrameRGB = avcodec_alloc_frame();

//         int src_bytes_num = avpicture_get_size(srcfmt, nWidth, nHeight);
//         uint8_t* src_buff = (uint8_t*)av_malloc(src_bytes_num);
//         avpicture_fill((AVPicture*)pFrame, src_buff, srcfmt, nWidth, nHeight);

        int dst_bytes_num = avpicture_get_size(dstfmt, nWidth, nHeight);
        uint8_t* dst_buff = (uint8_t*)av_malloc(dst_bytes_num);
        avpicture_fill((AVPicture*)pFrameRGB, dst_buff, dstfmt, nWidth, nHeight);

        SwsContext* pSwsCtx = sws_getContext(nWidth, nHeight,
            srcfmt,
            nWidth, nHeight,
            dstfmt,
            SWS_BICUBIC,
            NULL,NULL,NULL);

        /// <轉換圖像格式>
        sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, nHeight, pFrameRGB->data, pFrameRGB->linesize);

        static int nnn = 0;
        
        if ((nnn++ % 5) == 0)
        {
            //saveAsBitmap(pFrameRGB, nWidth, nHeight, nnn);
        }
    }

the_end:
    avcodec_flush_buffers(is->video_st->codec);
    av_free_packet(&pkt);
    avcodec_free_frame(&pFrame);
    return 0;
}


bool saveAsBitmap(AVFrame *pFrameRGB, int width, int height, int iFrame)  
{  
    if (NULL == pFrameRGB->data[0])
    {
        return false;
    }

    FILE *pFile = NULL;  
    BITMAPFILEHEADER bmpheader;  
    BITMAPINFO bmpinfo;  

    char fileName[32];  
    int bpp = 24;  

    // open file  
    sprintf(fileName, "./images/frame%d.bmp", iFrame);  
    pFile = fopen(fileName, "wb");  
    if (!pFile)  
        return false;  

    bmpheader.bfType = ('M' <<8)|'B';  
    bmpheader.bfReserved1 = 0;  
    bmpheader.bfReserved2 = 0;  
    bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);  
    bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;  

    bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);  
    bmpinfo.bmiHeader.biWidth = width;  
    bmpinfo.bmiHeader.biHeight = -height; //reverse the image  
    bmpinfo.bmiHeader.biPlanes = 1;  
    bmpinfo.bmiHeader.biBitCount = bpp;  
    bmpinfo.bmiHeader.biCompression = BI_RGB;  
    bmpinfo.bmiHeader.biSizeImage = 0;  
    bmpinfo.bmiHeader.biXPelsPerMeter = 100;  
    bmpinfo.bmiHeader.biYPelsPerMeter = 100;  
    bmpinfo.bmiHeader.biClrUsed = 0;  
    bmpinfo.bmiHeader.biClrImportant = 0;  

    fwrite(&bmpheader, sizeof(BITMAPFILEHEADER), 1, pFile);  
    fwrite(&bmpinfo.bmiHeader, sizeof(BITMAPINFOHEADER), 1, pFile);  
     uint8_t *buffer = pFrameRGB->data[0];  
//     for (int h=0; h<height; h++)  
//     {  
//         for (int w=0; w<width; w++)  
//         {  
//             fwrite(buffer+2, 1, 1, pFile);  
//             fwrite(buffer+1, 1, 1, pFile);  
//             fwrite(buffer, 1, 1, pFile);  
// 
//             buffer += 3;  
//         }  
//     }  

    fwrite(buffer,width*height*bpp/8,1,pFile);

    fclose(pFile);  

    return true;  


chenjt3533 2014-08-21 22:21 發表評論
]]>
c++ 獲取漢字首字母http://www.shnenglu.com/chenjt3533/archive/2013/05/31/200716.htmlchenjt3533chenjt3533Fri, 31 May 2013 05:01:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/05/31/200716.htmlhttp://www.shnenglu.com/chenjt3533/comments/200716.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/05/31/200716.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/200716.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/200716.html
#include 
<iostream>
#include 
<string>
using namespace std;

#define CONVERT(start, end, code, letter) if(code >= start && code <= end) return letter

char Convert(wchar_t n)
{
    
/// 根據漢字區域碼獲取拼音聲母
    CONVERT(0xB0A10xB0C4, n, 'a');
    CONVERT(
0XB0C50XB2C0, n, 'b');
    CONVERT(
0xB2C10xB4ED, n, 'c');
    CONVERT(
0xB4EE0xB6E9, n, 'd');
    CONVERT(
0xB6EA0xB7A1, n, 'e');
    CONVERT(
0xB7A20xB8c0, n, 'f');
    CONVERT(
0xB8C10xB9FD, n, 'g');
    CONVERT(
0xB9FE0xBBF6, n, 'h');
    CONVERT(
0xBBF70xBFA5, n, 'j');
    CONVERT(
0xBFA60xC0AB, n, 'k');
    CONVERT(
0xC0AC0xC2E7, n, 'l');
    CONVERT(
0xC2E80xC4C2, n, 'm');
    CONVERT(
0xC4C30xC5B5, n, 'n');
    CONVERT(
0xC5B60xC5BD, n, 'o');
    CONVERT(
0xC5BE0xC6D9, n, 'p');
    CONVERT(
0xC6DA0xC8BA, n, 'q');
    CONVERT(
0xC8BB0xC8F5, n, 'r');
    CONVERT(
0xC8F60xCBF0, n, 's');
    CONVERT(
0xCBFA0xCDD9, n, 't');
    CONVERT(
0xCDDA0xCEF3, n, 'w');
    CONVERT(
0xCEF40xD188, n, 'x');
    CONVERT(
0xD1B90xD4D0, n, 'y');
    CONVERT(
0xD4D10xD7F9, n, 'z');
    
return '\0';
}


void Test_Invert()
{
    
string sChinese = "根據漢字區域碼獲取漢字首字母"// 輸入漢字

    wchar_t wchr 
= 0;
    
int nCount = sChinese.length() / 2;
    
char* buff = new char[nCount];
    memset(buff, 
0x00sizeof(char)*nCount+1);

    
for (int i = 0, j = 0; i < nCount; ++i)
    
{
        wchr 
= (sChinese[j++& 0xff<< 8// 高字節
        wchr |= (sChinese[j++& 0xff);        // 低字節
        buff[i] = Convert(wchr);
    }


    cout 
<< "pin yin = [ " << buff << " ]" << endl; 
}








chenjt3533 2013-05-31 13:01 發表評論
]]>
《c Templates 》筆記http://www.shnenglu.com/chenjt3533/archive/2013/05/30/200696.htmlchenjt3533chenjt3533Thu, 30 May 2013 07:36:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/05/30/200696.htmlhttp://www.shnenglu.com/chenjt3533/comments/200696.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/05/30/200696.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/200696.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/200696.html1、Function Templates(函數模版)

例:
tempalte <typename T>
T GetMax(T a, T b)
{
 return a < b ? b : a;
}

/// 重載
tempalte <typename T>
T GetMax(T a, T b, T c)
{
 return (c < GetMax(a, b) ? GetMax(a, b) : c);
}

/// 默認類型
template <typename T=int>
T GetMax(T a, T b)
{
 retrun a < b ? b : a;
}

2、Class Templates(類模版)
例:
template <typename T>
class MyStack
{
 friend class CFriend1;   // 普通友元類不需要先申明
 friend class CFriend2<T>; // error, 友元模板類必須先申明

 public:
  void Push(T const&);
  void Pop();
  T Top() const;
  bool Empty() const;
 private:
  std::vector<T> elems;
};

void MyStack<T>::Push(T const& e)
{
 elems.push_back(e);
}

/// 繼承
template <typename T>
class Derived : public MyStack<T>
{
public:
 void Test()
 {
  Top(); // 應該使用this->Top() 或 MyStack<T>::Top(), 否則就調用外部Top(),或者編譯錯誤
 }

 /// 成員模版函數,不能是virtual
 template <typename T2>
 void Test2()
 {
  
 }
};


3、NonType Templates(非類型模版參數)
局限性:通常只能是常數整數(包括enum)和指向局部變量的指針。
例:
template <typename T, int MAXSIZE>
struct NonTypeStruct
{
 T elems[MAXSIZE];
};


4、typename 關鍵字
typename是在C++標準化工程中被引入的,目的是向編譯器說明template內部的某個表示符號是個類型。
例:
template<typename T>
struct MyStruct
{
 typename T::SubType* m_Ptr; // 表示m_Ptr是指向SubType是T內部類型的一個指針,若沒有typename,就表

示T的靜態變量SubType乘以m_Ptr。
};

5、雙重模版參數
例:
template <typename T, template <typename> class CT>
class DoubleTemplate
{
 CT<t> m_ct; 
};
DoubleTemplate<int, std::vector> dbTemplate; // 類中定義了一個vector<int>的成員屬性m_ct;

6、模板的申明和定義必須在同一個文檔中,否則會出現連接錯誤
例:
// Test.h
template <typename T>
T Max(T a);

// Test.cpp
template <typename T>
T Max(T a) { return a; }

// Main.cpp
#include "Test.h"

void main()
{
 int a = Max<int>(1); // error LNK2019,除非同時 #inclde "test.cpp"
}

7、模版類不能和其它不同類型的實體同名。
例:
int c;
class c; // ok,普通類可以
template <typename T>
class c; // error,模板類不行

8、在類中聲明友元模版類
例:
class Manager
{
 template <typename T>
 friend class Task;
};

9、看看小細節
例:
template <bool b>
class Invert
{
 public:
  static bool const result = !b;
};
bool bTest = Invert<(1>0)>::result; // (1>0)小括號必須存在

std::vector<std::list<int> > vectList; // <std::list<int> >空格必須存在



chenjt3533 2013-05-30 15:36 發表評論
]]>
C++模板簡單分析與舉例http://www.shnenglu.com/chenjt3533/archive/2013/03/30/198947.htmlchenjt3533chenjt3533Sat, 30 Mar 2013 06:23:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/30/198947.htmlhttp://www.shnenglu.com/chenjt3533/comments/198947.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/30/198947.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198947.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198947.html#pragma once#include <iostream>/**//*/////////////////////////////////////////////&nbs...  閱讀全文

chenjt3533 2013-03-30 14:23 發表評論
]]>
四種查找算法的簡單分析與實現http://www.shnenglu.com/chenjt3533/archive/2013/03/28/198899.htmlchenjt3533chenjt3533Thu, 28 Mar 2013 11:57:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/28/198899.htmlhttp://www.shnenglu.com/chenjt3533/comments/198899.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/28/198899.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198899.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198899.html#pragma once/**//*             &...  閱讀全文

chenjt3533 2013-03-28 19:57 發表評論
]]>
七種排序算法的簡單分析與實現http://www.shnenglu.com/chenjt3533/archive/2013/03/25/198815.htmlchenjt3533chenjt3533Mon, 25 Mar 2013 13:01:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/25/198815.htmlhttp://www.shnenglu.com/chenjt3533/comments/198815.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/25/198815.html#Feedback4http://www.shnenglu.com/chenjt3533/comments/commentRss/198815.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198815.html#pragma once/**//**//**//*            ...  閱讀全文

chenjt3533 2013-03-25 21:01 發表評論
]]>
KMP 匹配算法http://www.shnenglu.com/chenjt3533/archive/2013/03/20/198641.htmlchenjt3533chenjt3533Wed, 20 Mar 2013 11:06:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/20/198641.htmlhttp://www.shnenglu.com/chenjt3533/comments/198641.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/20/198641.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198641.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198641.html

一:BF算法
     如果讓你寫字符串的模式匹配,你可能會很快的寫出樸素的bf算法,至少問題是解決了,我想大家很清楚的知道它的時間復雜度為O(MN),原因很簡單,主串和模式串失配的時候,我們總是將模式串的第一位與主串的下一個字符進行比較,所以復雜度高在主串每次失配的時候都要回溯,圖我就省略了。

 二:KMP算法
   剛才我們也說了,主串每次都要回溯,從而提高了時間復雜度,那么能不能在“主串”和“模式串”失配的情況下,主串不回溯呢?而是讓”模式串“向右滑動一定的距離,對上號后繼續進行下一輪的匹配,從而做到時間復雜度為O(M+N)呢?所以kmp算法就是用來處理這件事情的。


代碼:
#pragma once

#include <iostream>

//                --- 樸素匹配算法 --- 

int NaiveMatch(const char * txt, const char * pat)  
{
    int  nTxtLength = strlen(txt);
    int  nPatLength = strlen(pat);
    int  nTxt = 0;
    int  nPat = 0;
    int nCount = 0; // 計數

    while (nTxt <= nTxtLength - nPatLength && nPat < nPatLength)  
    {
        ++nCount;

        if (pat[nPat] == txt[nTxt+nPat])
        {
            ++nPat;
        }
        else
        {
            ++nTxt;
            nPat = 0;
        }
    }

    std::cout << "NaiveMatch, compare counts: " << nCount << std::endl;

    return  (nPat == nPatLength ? nTxt : -1);




//                --- KMP 匹配算法 ---

void GetNext(const char* pattern, int next[])
{
    next[0] = 0;

    int j = 0;
    for (int i = 1; i < strlen(pattern); i++)
    {
        while( j > 0 && pattern[j] != pattern[i] )
        {
            j = next[j-1];
        }

        if (pattern[j] == pattern[i])
            j++;

        next[i] = j;
    }
}

int KMPMatch(const char* src, const char* pattern, int next[])
{
    int nSrc = 0;
    int nPattern = 0;
    int nCount = 0; // 計數

    while(nSrc < strlen(src) && nPattern < strlen(pattern))
    {
        ++nCount;
        if (0 == nPattern || src[nSrc] == pattern[nPattern])
        {
            ++nPattern;
            ++nSrc;
        }
        else
        {
            nPattern = next[nPattern];
        }
    }

    std::cout << "KMP, compare counts: " << nCount << std::endl;

    return (nPattern == strlen(pattern) ? nSrc - nPattern : -1);
}



#define PRINT_RESLUT(v) { if(-1 == (v)) std::cout << "No Find!"; else std::cout << "Position = " << (v); std::cout << std::endl << std::endl; }

int main()
{
    const char* Src        = "abc1aabc1aabc1aaababc1aababc1aabc1aaabc1aabc1aababc1aaabaabcbc1aa";
    const char* Pattern = "abaabc";

    int nPos = -1; // 保存匹配到的位置

    /// 樸素匹配算法
    nPos = NaiveMatch(Src, Pattern);
    PRINT_RESLUT(nPos);

    /// KMP匹配算法
    int next[6] = {0};
    GetNext(Pattern, next);
    nPos = KMPMatch(Src, Pattern, next);
    PRINT_RESLUT(nPos);
   
   system("pause");
   return 0;     
}

運行結果:


疑惑:為什么KMP的效率會更低?


chenjt3533 2013-03-20 19:06 發表評論
]]>
C語言 malloc 工作機制http://www.shnenglu.com/chenjt3533/archive/2013/03/19/198591.htmlchenjt3533chenjt3533Tue, 19 Mar 2013 09:08:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/19/198591.htmlhttp://www.shnenglu.com/chenjt3533/comments/198591.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/19/198591.html#Feedback1http://www.shnenglu.com/chenjt3533/comments/commentRss/198591.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198591.html
void *malloc (size_t stSize);
該函數在內存的動態存儲區中分配 stSize 連續空間,返回值是一個指向所分配的連續存儲域的起始地址的指針。

void free(void *firstbyte);
如果給定一個由先前的 malloc 返回的指針,那么該函數會將分配的空間歸還給進程的“空閑空間”。

malloc 工作機制:
malloc函數的實質體現在,它有一個將可用的內存塊連接為一個長長的列表的所謂空閑鏈表。調用malloc函數時,它沿連接表尋找一個大到足以滿足用戶請求所需要的內存塊。然后,將該內存塊一分為二(一塊的大小與用戶請求的大小相等,另一塊的大小就是剩下的字節)。接下來,將分配給用戶的那塊內存傳給用戶,并將剩下的那塊(如果有的話)返回到連接表上。調用free函數時,它將用戶釋放的內存塊連接到空閑鏈上。到最后,空閑鏈會被切成很多的小內存片段,如果這時用戶申請一個大的內存片段,那么空閑鏈上可能沒有可以滿足用戶要求的片段了。于是,malloc函數請求延時,并開始在空閑鏈上翻箱倒柜地檢查各內存片段,對它們進行整理,將相鄰的小空閑塊合并成較大的內存塊。

參考:http://www.cnblogs.com/xkfz007/articles/2729027.html


chenjt3533 2013-03-19 17:08 發表評論
]]>
類模板 與 模板類http://www.shnenglu.com/chenjt3533/archive/2013/03/13/198387.htmlchenjt3533chenjt3533Wed, 13 Mar 2013 09:11:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/13/198387.htmlhttp://www.shnenglu.com/chenjt3533/comments/198387.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/13/198387.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198387.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198387.html
類模板:類是對象的抽象,而類模板又是類的抽象,可以用模板定義出具體類(模板類)。
模板類:就是用模板定義出的具體類。
我們知道c++的多態有兩種,一是運行時的多態,也就是虛函數產生多態;二就是編譯時的多態,它就是由類模板產生的多態。

例子:
#include <iostream>

/// 一個類模板
template <typename T> // typename 也可以用 class
class Base
{
public:
    void PrintTypeName() // 打印出 T 的類型
    { 
        std::cout << typeid(T).name() << std::endl;
    }
};

typedef Base<int>  IntBase;        // 一個模板類
typedef Base<char> CharBase;    // 另一個模板類

int main()
{
    IntBase* pIntBase = new IntBase;
    if (NULL != pIntBase)
    {
        pIntBase->PrintTypeName();
        delete pIntBase;
        pIntBase = NULL;
    }

    CharBase* pCharBase = new CharBase;
    if (NULL != pCharBase)
    {
        pCharBase->PrintTypeName();
        delete pCharBase;
        pCharBase = NULL;
    }

    system("pause");
    return 0;
}


chenjt3533 2013-03-13 17:11 發表評論
]]>
區分重載(overload),覆蓋(Override)和隱藏(hide)http://www.shnenglu.com/chenjt3533/archive/2013/03/08/198302.htmlchenjt3533chenjt3533Fri, 08 Mar 2013 09:29:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/08/198302.htmlhttp://www.shnenglu.com/chenjt3533/comments/198302.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/08/198302.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198302.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198302.htmlhttp://blog.csdn.net/jixingzhong/article/details/1858943 

重載overload,這個概念是大家熟知的。在同一可訪問區內被聲名的幾個具有不同參數列的(參數的類型、個數、順序不同)同名函數,程序會根據不同的參數列來確定具體調用哪個函數,這種機制就是重載。重載不關心函數的返回值類型,即返回類型不同無法構成重載。此外,C++ 中的const成員函數也可以構成overload。
    總結一下重載的特征:
  1、處在相同的空間中,即相同的范圍內;
  2、函數名相同;
  3、參數不同,即參數個數不同,或相同位置的參數類型不同;
  4、const成員函數可以和非const成員函數形成重載;
        5、virtual關鍵字、返回類型對是否夠成重載無任何影響。


    覆蓋override,是指派生類中存在重新定義的函數,其函數名、參數列、返回值類型必須同父類中的相對應被覆蓋的函數嚴格一致,覆蓋函數和被覆蓋函數只有函數體(花括號中的部分)不同,當派生類對象調用子類中該同名函數時會自動調用子類中的覆蓋版本,而不是父類中的被覆蓋函數版本,這種機制就叫做覆蓋,特征是:       
        1、不同的范圍(分別位于派生類與基類);       
        2、函數名字相同;       
        3、參數相同;       
        4、基類函數必須有virtual關鍵字。  

 

針對上述兩個概念,還有一個隱藏hide。所謂的隱藏,指的是派生類類型的對象、指針、引用訪問基類和派生類都有的同名函數時,訪問的是派生類的函數,即隱藏了基類的同名函數。隱藏規則的底層原因其實是C++的名字解析過程。在繼承機制下,派生類的類域被嵌套在基類的類域中。派生類的名字解析過程如下:
  1、首先在派生類類域中查找該名字。
  2、如果第一步中沒有成功查找到該名字,即在派生類的類域中無法對該名字進行解析,則編譯器在外圍基類類域對查找該名字的定義。
    總結一下隱藏的特征:
        1、如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。       
        2、如果派生類的函數與基類的函數同名,并且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。 



chenjt3533 2013-03-08 17:29 發表評論
]]>
c++ 函數重載http://www.shnenglu.com/chenjt3533/archive/2013/03/07/198257.htmlchenjt3533chenjt3533Thu, 07 Mar 2013 03:13:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/07/198257.htmlhttp://www.shnenglu.com/chenjt3533/comments/198257.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/07/198257.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198257.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198257.html

——每個現象后面都隱藏一個本質,關鍵在于我們是否去挖掘

寫在前面:

函數重載的重要性不言而明,但是你知道C++中函數重載是如何實現的呢(雖然本文談的是C++中函數重載的實現,但我想其它語言也是類似的)?這個可以分解為下面兩個問題

  • 1、聲明/定義重載函數時,是如何解決命名沖突的?(拋開函數重載不談,using就是一種解決命名沖突的方法,解決命名沖突還有很多其它的方法,這里就不論述了)
  • 2、當我們調用一個重載的函數時,又是如何去解析的?(即怎么知道調用的是哪個函數呢)

這兩個問題是任何支持函數重載的語言都必須要解決的問題!帶著這兩個問題,我們開始本文的探討。本文的主要內容如下:

  •  1、例子引入(現象)
    • 什么是函數重載(what)?
    • 為什么需要函數重載(why)?
  • 2、編譯器如何解決命名沖突的?
    • 函數重載為什么不考慮返回值類型
  • 3、重載函數的調用匹配
    • 模凌兩可的情況
  • 4、編譯器是如何解析重載函數調用的?
    • 根據函數名確定候選函數集
    • 確定可用函數
    • 確定最佳匹配函數
  • 5、總結

1、例子引入(現象)

1.1、什么是函數重載(what)?

函數重載是指在同一作用域內,可以有一組具有相同函數名不同參數列表的函數,這組函數被稱為重載函數。重載函數通常用來命名一組功能相似的函數,這樣做減少了函數名的數量,避免了名字空間的污染,對于程序的可讀性有很大的好處。

When two or more different declarations are specified for a single name in the same scope,  that name is said to overloaded.  By extension, two declarations in the same scope that declare the same name but with different types are called overloaded declarations. Only function declarations can be overloaded; object and type declarations cannot be overloaded. ——摘自《ANSI C++ Standard. P290》

看下面的一個例子,來體會一下:實現一個打印函數,既可以打印int型、也可以打印字符串型。在C++中,我們可以這樣做:

#include<iostream> 
using namespace std;  
void print(int i) 
{         
cout
<<"print a integer :"<<i<<endl; 

void print(string str) 
{         
cout
<<"print a string :"<<str<<endl; 

int main() 
{         
print(
12);        
print(
"hello world!");
return 0
}

通過上面代碼的實現,可以根據具體的print()的參數去調用print(int)還是print(string)。上面print(12)會去調用print(int),print("hello world")會去調用print(string),如下面的結果:(先用g++ test.c編譯,然后執行)

C  函數重載例子1

1.2、為什么需要函數重載(why)?

  • 試想如果沒有函數重載機制,如在C中,你必須要這樣去做:為這個print函數取不同的名字,如print_int、print_string。這里還只是兩個的情況,如果是很多個的話,就需要為實現同一個功能的函數取很多個名字,如加入打印long型、char*、各種類型的數組等等。這樣做很不友好!
  • 類的構造函數跟類名相同,也就是說:構造函數都同名。如果沒有函數重載機制,要想實例化不同的對象,那是相當的麻煩!
  • 操作符重載,本質上就是函數重載,它大大豐富了已有操作符的含義,方便使用,如+可用于連接字符串等!

通過上面的介紹我們對函數重載,應該喚醒了我們對函數重載的大概記憶。下面我們就來分析,C++是如何實現函數重載機制的。

2、編譯器如何解決命名沖突的?

為了了解編譯器是如何處理這些重載函數的,我們反編譯下上面我們生成的執行文件,看下匯編代碼(全文都是在Linux下面做的實驗,Windows類似,你也可以參考《一道簡單的題目引發的思考》一文,那里既用到Linux下面的反匯編和Windows下面的反匯編,并注明了Linux和Windows匯編語言的區別)。我們執行命令objdump -d a.out >log.txt反匯編并將結果重定向到log.txt文件中,然后分析log.txt文件。

發現函數void print(int i) 編譯之后為:(注意它的函數簽名變為——_Z5printi

image

發現函數void print(string str) 編譯之后為:(注意它的函數簽名變為——_Z5printSs

image

我們可以發現編譯之后,重載函數的名字變了不再都是print!這樣不存在命名沖突的問題了,但又有新的問題了——變名機制是怎樣的,即如何將一個重載函數的簽名映射到一個新的標識?我的第一反應是:函數名+參數列表,因為函數重載取決于參數的類型、個數,而跟返回類型無關。但看下面的映射關系:

void print(int i)                    -->         _Z5printi 
void print(string str)         -->         _Z5printSs

進一步猜想,前面的Z5表示返回值類型,print函數名,i表示整型int,Ss表示字符串string,即映射為返回類型+函數名+參數列表。最后在main函數中就是通過_Z5printi_Z5printSs來調用對應的函數的:

80489bc:       e8 73 ff ff ff          call   8048934 <_Z5printi> 
…………… 
80489f0:       e8 7a ff ff ff          call   804896f <_Z5printSs>

我們再寫幾個重載函數來驗證一下猜想,如:

void print(long l)           -->           _Z5printl 
void print(char str)      -->           _Z5printc 
可以發現大概是int->i,long->l,char->c,string->Ss….基本上都是用首字母代表,現在我們來現在一個函數的返回值類型是否真的對函數變名有影響,如:

#include<iostream> using namespace std;
int max(int a,int b) 
{         
return a>=b?a:b; 
}  
double max(double a,double b) 
{         
return a>=b?a:b; 

int main() 
{         
cout
<<"max int is: "<<max(1,3)<<endl;         
cout
<<"max double is: "<<max(1.2,1.3)<<endl;         return 0;
}

int max(int a,int b) 映射為_Z3maxiidouble max(double a,double b) 映射為_Z3maxdd,這證實了我的猜想,Z后面的數字代碼各種返回類型。更加詳細的對應關系,如那個數字對應那個返回類型,哪個字符代表哪重參數類型,就不去具體研究了,因為這個東西跟編譯器有關,上面的研究都是基于g++編譯器,如果用的是vs編譯器的話,對應關系跟這個肯定不一樣。但是規則是一樣的:“返回類型+函數名+參數列表”。

既然返回類型也考慮到映射機制中,這樣不同的返回類型映射之后的函數名肯定不一樣了,但為什么不將函數返回類型考慮到函數重載中呢?——這是為了保持解析操作符或函數調用時,獨立于上下文(不依賴于上下文),看下面的例子

float sqrt(float); 
double sqrt(double);  
void f(double da, float fla) 
{       
float fl=sqrt(da);//調用sqrt(double)       
double d=sqrt(da);//調用sqrt(double)        
fl=sqrt(fla);//調用sqrt(float)
d=sqrt(fla);//調用sqrt(float) 
}

如果返回類型考慮到函數重載中,這樣將不可能再獨立于上下文決定調用哪個函數。

至此似乎已經完全分析清楚了,但我們還漏了函數重載的重要限定——作用域。上面我們介紹的函數重載都是全局函數,下面我們來看一下一個類中的函數重載,用類的對象調用print函數,并根據實參調用不同的函數:

#include<iostream> 
using namespace std;  
class test

public:         
void print(int i)         
{                 
cout
<<"int"<<endl;         
}         
void print(char c)         
{                 
cout
<<"char"<<endl;         

}; 
int main() 
{         
test t;         
t.print(
1);         
t.print(
'a');         
return 0;
 }

我們現在再來看一下這時print函數映射之后的函數名:

void print(int i)                    -->            _ZN4test5printEi

void print(char c)               -->            _ZN4test5printEc

注意前面的N4test,我們可以很容易猜到應該表示作用域,N4可能為命名空間、test類名等等。這說明最準確的映射機制為:作用域+返回類型+函數名+參數列表

3、重載函數的調用匹配

現在已經解決了重載函數命名沖突的問題,在定義完重載函數之后,用函數名調用的時候是如何去解析的?為了估計哪個重載函數最適合,需要依次按照下列規則來判斷:

  • 精確匹配:參數匹配而不做轉換,或者只是做微不足道的轉換,如數組名到指針、函數名到指向函數的指針、T到const T;
  • 提升匹配:即整數提升(如bool 到 int、char到int、short 到int),float到double
  • 使用標準轉換匹配:如int 到double、double到int、double到long double、Derived*到Base*、T*到void*、int到unsigned int;
  • 使用用戶自定義匹配
  • 使用省略號匹配:類似printf中省略號參數

如果在最高層有多個匹配函數找到,調用將被拒絕(因為有歧義、模凌兩可)。看下面的例子:


定義太少或太多的重載函數,都有可能導致模凌兩可,看下面的一個例子:

void f1(char); void f1(long);  void f2(char*); void f2(int*);  void k(int i) {        f1(i);//調用f1(char)? f1(long)?        f2(0);//調用f2(char*)?f2(int*)? }

這時侯編譯器就會報錯,將錯誤拋給用戶自己來處理:通過顯示類型轉換來調用等等(如f2(static_cast<int *>(0),當然這樣做很丑,而且你想調用別的方法時有用做轉換)。上面的例子只是一個參數的情況,下面我們再來看一個兩個參數的情況:

int pow(int ,int); double pow(double,double);  void g() {        double d=pow(2.0,2)//調用pow(int(2.0),2)? pow(2.0,double(2))? }

4、編譯器是如何解析重載函數調用的?

編譯器實現調用重載函數解析機制的時候,肯定是首先找出同名的一些候選函數,然后從候選函數中找出最符合的,如果找不到就報錯。下面介紹一種重載函數解析的方法:編譯器在對重載函數調用進行處理時,由語法分析、C++文法、符號表、抽象語法樹交互處理,交互圖大致如下:

image 

這個四個解析步驟所做的事情大致如下:

  • 由匹配文法中的函數調用,獲取函數名;
  • 獲得函數各參數表達式類型;
  • 語法分析器查找重載函數,符號表內部經過重載解析返回最佳的函數
  • 語法分析器創建抽象語法樹,將符號表中存儲的最佳函數綁定到抽象語法樹上

 

下面我們重點解釋一下重載解析,重載解析要滿足前面《3、重載函數的調用匹配》中介紹的匹配順序和規則。重載函數解析大致可以分為三步:

  • 根據函數名確定候選函數集
  • 從候選函數集中選擇可用函數集合
  • 從可用函數集中確定最佳函數,或由于模凌兩可返回錯誤

4.1、根據函數名確定候選函數集

根據函數在同一作用域內所有同名的函數,并且要求是可見的(像private、protected、public、friend之類)。“同一作用域”也是在函數重載的定義中的一個限定,如果不在一個作用域,不能算是函數重載,如下面的代碼:

void f(int);  void g() {         void f(double);         f(1); //這里調用的是f(double),而不是f(int) }

內層作用域的函數會隱藏外層的同名函數同樣的派生類的成員函數會隱藏基類的同名函數。這很好理解,變量的訪問也是如此,如一個函數體內要訪問全局的同名變量要用“::”限定。

為了查找候選函數集,一般采用深度優選搜索算法:

step1:從函數調用點開始查找,逐層作用域向外查找可見的候選函數 
step2:如果上一步收集的不在用戶自定義命名空間中,則用到了using機制引入的命名空間中的候選函數,否則結束

在收集候選函數時,如果調用函數的實參類型為非結構體類型,候選函數僅包含調用點可見的函數;如果調用函數的實參類型包括類類型對象、類類型指針、類類型引用或指向類成員的指針,候選函數為下面集合的并:

  • (1)在調用點上可見的函數;
  • (2)在定義該類類型的名字空間或定義該類的基類的名字空間中聲明的函數;
  • (3)該類或其基類的友元函數;

下面我們來看一個例子更直觀:

void f(); void f(int); void f(double, double = 314); names pace N {      void f(char3 ,char3); } classA{     public: operat or double() { } }; int main ( ) {     using names pace N; //using指示符     A a;     f(a);     return 0; }

 

根據上述方法,由于實參是類類型的對象,候選函數的收集分為3步:

(1)從函數調用所在的main函數作用域內開始查找函數f的聲明, 結果未找到。到main函數 
作用域的外層作用域查找,此時在全局作用域找到3個函數f的聲明,將它們放入候選集合;

(2)到using指示符所指向的命名空間 N中收集f ( char3 , char3 ) ;

(3)考慮2類集合。其一為定義該類類型的名字空間或定義該類的基類的名字空間中聲明的函 
數;其二為該類或其基類的友元函數。本例中這2類集合為空。

最終候選集合為上述所列的 4個函數f。

4.2、確定可用函數

可用的函數是指:函數參數個數匹配并且每一個參數都有隱式轉換序列。

  • (1)如果實參有m個參數,所有候選參數中,有且只有 m個參數;
  • (2)所有候選參數中,參數個數不足m個,當前僅當參數列表中有省略號;
  • (3)所有候選參數中,參數個數超過 m個,當前僅當第m + 1個參數以后都有缺省值。如果可用 
    集合為空,函數調用會失敗。

這些規則在前面的《3、重載函數的調用匹配》中就有所體現了。

4.3、確定最佳匹配函數

確定可用函數之后,對可用函數集中的每一個函數,如果調用函數的實參要調用它計算優先級,最后選出優先級最高的。如對《3、重載函數的調用匹配》中介紹的匹配規則中按順序分配權重,然后計算總的優先級,最后選出最優的函數。

 

5、總結

本文介紹了什么是函數重載、為什么需要函數重載、編譯器如何解決函數重名問題、編譯器如何解析重載函數的調用。通過本文,我想大家對C++中的重載應該算是比較清楚了。說明:在介紹函數名映射機制是基于g++編譯器,不同的編譯器映射有些差別;編譯器解析重載函數的調用,也只是所有編譯器中的一種。如果你對某個編譯器感興趣,請自己深入去研究。

最后我拋給大家兩個問題:

  • 1、在C++中加號+,即可用于兩個int型之間的相加、也可以用于浮點數數之間的相加、字符串之間的連接,那+算不算是操作符重載呢?換個場景C語言中加號+,即可用于兩個int型之間的相加、也可以用于浮點數數之間的相加,那算不算操作符重載呢?
  • 2、模板(template)的重載時怎么樣的?模板函數和普通函數構成的重載,調用時又是如何匹配的呢?

附錄:一種C++函數重載機制

這個機制是由張素琴等人提出并實現的,他們寫了一個C++的編譯系統COC++(開發在國產機上,UNIX操作系統環境下具有中國自己版權的C、C++和FORTRAN語言編譯系統,這些編譯系統分別滿足了ISOC90、AT&T的C++85和ISOFORTRAN90標準)。COC++中的函數重載處理過程主要包括兩個子過程:

  • 1、在函數聲明時的處理過程中,編譯系統建立函數聲明原型鏈表,按照換名規則進行換名并在函數聲明原型鏈表中記錄函數換名后的名字(換名規則跟本文上面描述的差不多,只是那個int-》為哪個字符、char-》為哪個字符等等類似的差異)

image

圖附1、過程1-建立函數鏈表(說明,函數名的編碼格式為:<原函數名>_<作用域換名><函數參數表編碼>,這跟g++中的有點不一樣)

  • 2、在函數調用語句翻譯過程中,訪問符號表,查找相應函數聲明原型鏈表,按照類型匹配原則,查找最優匹配函數節點,并輸出換名后的名字下面給出兩個子過程的算法建立函數聲明原型鏈表算法流程如圖附1,函數調用語句翻譯算法流程如圖附2。

image

圖附2、過程2- 重載函數調用,查找鏈表

附-模板函數和普通函數構成的重載,調用時又是如何匹配的呢?

下面是C++創始人Bjarne Stroustrup的回答:

1)Find the set of function template specializations that will take part in overload resolution.

2)if two template functions can be called and one is more specified than the other, consider only the most specialized template function in the following steps.

3)Do overload resolution for this set of functions, plus any ordinary functions as for ordinary functions.

4)If a function and a specialization are equally good matches, the function is perferred.

5)If no match is found, the call is an error.

 


作者:吳秦
出處:http://www.cnblogs.com/skynet/



chenjt3533 2013-03-07 11:13 發表評論
]]>
C++操作符重載http://www.shnenglu.com/chenjt3533/archive/2013/03/07/198256.htmlchenjt3533chenjt3533Thu, 07 Mar 2013 03:07:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/07/198256.htmlhttp://www.shnenglu.com/chenjt3533/comments/198256.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/07/198256.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198256.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198256.html
//原文鏈接: http://blog.csdn.net/xiegenwendada/article/details/8477209 

#include<stdio.h>  
#include <stdlib.h>  

/*///////////////////////////// 操作符重載 ////////////////////////////////////////////
-
-    操作符重載必須是,參數類型或個數不同,還有是否為const,但不以返回值的類型左判斷。
-
-    ------------------------------------------------------------------------------------    
-    |            不能重載的操作符號            |            可以重載的操作符                |
-    ------------------------------------------------------------------------------------
-    |    . .* :: ?: new delete sizeof        |    new new[] delete delete[] + - * / % ^    |
-    |    typeid static_cast dynamic_cast        |    & | ~ ! = < > += -= *= /= %=  ^= &= |=    |
-    |    const_cast reintERPret_cast            |    << >> >>= <<= == != <= >= && || ++ --    |
-    |                                        |    ->* -> () []                            |
-    -------------------------------------------------------------------------------------
-    
-
-    ------------------------------------------------------------------------------------    
-    |            類成員操作符重載            |            友員函數操作符重載                |
-    ------------------------------------------------------------------------------------
-    |        左右操作數都是該類對象            |            左操作數為其它類型                |
-    | 必須為類成員操作符:                    |                                            |
-    | 賦值(=),下標([]),調用(())        |                                            |
-    | 和成員訪問箭頭(->)                    |                                            |
-    -------------------------------------------------------------------------------------
-
//////////////////////////////// 操作符重載 ////////////////////////////////////////////
*/

// 簡單的重載
class CBaseOperator  
{  

public:  
    int nData;        //測試的變量  

public:  
    CBaseOperator(int nData = 0):nData(nData)  
    {  
        nData++;  
        --nData;  
    }  


    CBaseOperator(const CBaseOperator& cBO)  
    {  
        nData = cBO.nData;  
    }  
    //重載=操作符,一般=操作符和拷貝構造函數是成對出現的。  
    const CBaseOperator& operator=(const CBaseOperator& cBO)  
    {  
        nData = cBO.nData;  
        return *this;  
    }  

public:  

    //重載+操作符,簡單的二元操作符重載是最常見也是比較簡單的。基本思路都是這樣,注意如果  
    
//操作符出現在左邊,則只能用友員了。這里了有幾個返回類型是CBaseOperator,return  
    
//語句中卻是兩個int相加,這種情況是可以通過的,編譯器會自動構建一個相應的對象返回,  
    
//前提是你的構造函數要有相應的參數,這里是int作為參數  
    int operator+(int nAdd) const  
    {  
        return nData + nAdd;  
    }  

    int operator+(int nAdd)  
    {  
        return nData + nAdd;  
    }  

    friend int operator+(int nAdd,const CBaseOperator& cAdd)  
    {  
        return nAdd + cAdd.nData;  
    }  

    CBaseOperator operator+(const CBaseOperator& cAdd) const  
    {  
        return nData + cAdd.nData;  
    }  

    //重載減法什么的也是一樣。就不寫了。哈哈  

    
//比較操作符重載==,!=,>, >=, <, <=總結:這里都是配套的操作一般來說如果寫一個  
    
//都會重載其他幾個,特別是==,!=。當然也有例外。哈哈。。  
    bool operator==(const CBaseOperator& cEqual) const  
    {  
        return nData == cEqual.nData;  
    }  
    bool operator == (int nEqual) const  

    {        

        return nData == nEqual;  
    }  
    friend bool operator ==(int nEqual, const CBaseOperator& cEqual)  
    {  
        return cEqual.nData == nEqual;  
    }  
    bool operator!=(const CBaseOperator& cEqual) const  
    {  
        return nData != cEqual.nData;  
    }  

    //其他的也就不寫了,基本一樣。哈哈  

    
//重載++,--操作符,因為++,--有兩種方式,一種是前增量(++XXX先改變自己,返回),  
    
//一種是后增量(改變自己,返回改變前的狀態)  
    
//因為重載是判斷參數的,為了能區別前增量和后增量,C++的設計者做了這樣的考慮。  
    
//就是重載后增量的時候在參數列表中加一個int類型參數,這樣就避免的重載的重復了。  
    
//在調用上,如果都重載,那么用int參數的是后增量++,沒有參數的是前增量++,  
    
//(注:我在這里說的是成員函數,當然友員的重載參數個數要多一個,以后的我可別說我無知啊。)  
    
//如果都重載,那么前增量和后增量都會調用相應的函數,如果只重載了后增量,那么前增量會失敗  
    
//如果只重載了前增量,就會無論是前增量和后增量都會調用這個函數。所以一般他們也是成對  
    
//出現的,除非你懶,只寫前增量,可惜如果人家要調用后增量呢?結果會錯的哦。哈哈。  

    
//重載后增量.  
    CBaseOperator operator++(int)  
    {  
        CBaseOperator cTemp = *this;  
        ++nData;  
        return cTemp;  
    }  

    //重載前增量  
    CBaseOperator& operator++()  
    {  
        ++nData;  
        return *this;  
    }  

    //重載--操作符是一樣的,也不寫了。  

    
//重載[],()等操作符號,同樣[]的參數個數是確定的。  
    
//我之說以把他們寫一起,是因為我錯誤的以為[]的參數個數是可以自己定義。錯了錯了。  
    
//知錯能改是好的,對了,()的參數個數是可以自己定義的。這個就給我們很大的發揮空間了。  
    
//都忘了[],() = 等操作符必須是成員函數,上面有寫。不能用友員和靜態成員函數  

    
//重載[]  
    int operator[](int nIndex) const  
    {  
        return nIndex;  
    }  

    //重載()  
    int operator()(int a) const  
    {  
        return a;  
    }  

    bool operator()(int a, int b) const  
    {  
        return a > b;  
    }  

    CBaseOperator operator()(int a, int b, int c)  
    {  
        return CBaseOperator(a+b+c+*this);  
    }  

    //重載*,->的操作符,*操作符就是相當于指針的*p;不過這里已經失去了原來的意義,他不是一個指針了。  
    
//但如果是想通過他來得到一些東西,也是可以的,特別在迭代器中常用這種方法。->也是和*配對出現的。  
    
//不過->操作符比較有意思,貌似和(*p).dddd真的差不多,所以返回的應該是一個結構的指針,我們這里  
    
//就返回了本身,當然可以返回任何結構的指針的。(并不是只能返回本身)。  

    
//重載*,這里參數個數是固定的,多寫一個就成了乘法的操作了。哈哈  
    int operator*() const  
    {  
        return nData;  
    }  

    //重載->  
    CBaseOperator* operator->()  
    {  
        return this;  
    }  

    //其他的例如&& || 這樣的操作符還是不重載的好。利用其原有的本性  

    
//重載new delete,這里編譯器做了一個限制,new必須返回void*類型, delete必須  
    
//返回void類型。(上面說過函數重載是不檢查返回類型的,和這里并沒有沖突,他只是限定了返回  
    
//類型,而不是只有返回類型不同的函數能重載,這個是編譯器做的工作,一定上確保new能更好的工作吧)  
    
//還有就是他們的參數個數都是可以自定義的。new 和 new[] 是兩個不同的操作符,所以還是要分別重載一下。  
    
//delete 和 delete[] 也是兩個不同的操作符。這里只重載了一個。  
    voidoperator new(size_t size)  
    {  

        return  malloc(size);  
    }  

    voidoperator new[](size_t size)  
    {  
        return  malloc(size);  
    }  

    void operator delete(void* P, unsigned int size)  
    {  
        size = 0;  
        free(P);  
    }  
};  

int test_OverLoad()  
{  
    const CBaseOperator cCo1(100);  

    //判斷+重載符  
    int nSum = cCo1 + 50;  
    printf("%d\n", nSum);  
    nSum = 50 + cCo1;  
    printf("%d\n", nSum);  

    //這里順便檢測一下拷貝構造函數  
    CBaseOperator co2(20);  
    CBaseOperator co3 = co2 + cCo1;  
    nSum = co3.nData;  
    printf("%d\n", nSum);  

    nSum = co3 + 60;  
    printf("%d\n", nSum);  

    //檢測+,和=操作符  
    co3 = 10 + cCo1 + co2 + 20;  
    nSum = co3.nData;  
    printf("%d\n", nSum);  

    //查看比較操作符  
    if (cCo1 == cCo1 && cCo1 == 100 && 100 == cCo1)  
    {  
        printf("True\n");  
    }  
    co3 = co2;  
    if (!(co3 != co2))  
    {  
        printf("True\n");  
    }  

    //增量操作符,cCo1是不能做這個操作的,因為他是常量  
    nSum = co2.nData;  

    printf("%d\n", nSum);  

    nSum = (co2++).nData;  
    printf("%d\n", nSum);  
    nSum = (++co2).nData;  
    printf("%d\n", nSum);  

    //測試[],  
    nSum = cCo1[45];  
    printf("%d\n", nSum);  

    //測試()  
    nSum = cCo1(50);  
    printf("%d\n", nSum);  

    if (cCo1(45, 23))  
    {  
        printf("True\n");  
    }  

    co2 = co3(10,20,30);  
    nSum = co2.nData;  
    printf("%d\n", nSum);  

    //測試*,這里co2并不是指針哦。只是重載了*的操作符  
    nSum = *co2;  
    printf("%d\n", nSum);  

    //測試->,這里也一樣。  
    nSum = co2->nData;  
    printf("%d\n", nSum);  

    //測試new new[] delete,  
    
//這里沒有測試輸出。單步就知道了。  
    CBaseOperator* pCb1 = new CBaseOperator();  
    CBaseOperator* pCb2 = new CBaseOperator[10];  
    delete pCb1;  
    delete pCb2;  
    system("pause");  
    return 0;  


chenjt3533 2013-03-07 11:07 發表評論
]]>
C++四種強制轉換http://www.shnenglu.com/chenjt3533/archive/2013/03/05/198235.htmlchenjt3533chenjt3533Tue, 05 Mar 2013 09:56:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/05/198235.htmlhttp://www.shnenglu.com/chenjt3533/comments/198235.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/05/198235.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198235.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198235.html1 static_cast < type-id > ( expression )用法:

該運算符把expression轉換為type-id類型,但沒有運行時類型檢查來保證轉換的安全性。它主要有如下幾種用法:

①用于類層次結構中基類(父類)和派生類(子類)之間指針或引用的轉換。

進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的;

進行下行轉換(把基類指針或引用轉換成派生類表示)時,由于沒有動態類型檢查,所以是不安全的。

②用于基本數據類型之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。

③把空指針轉換成目標類型的空指針。

④把任何類型的表達式轉換成void類型。

注意:static_cast不能轉換掉expression的const、volatile、或者__unaligned屬性。

C++中static_cast和reinterpret_cast的區別

C++primer第五章里寫了編譯器隱式執行任何類型轉換都可由static_cast顯示完成;reinterpret_cast通常為操作數的位模式提供較低層的重新解釋

1、C++中的static_cast執行非多態的轉換,用于代替C中通常的轉換操作。因此,被做為隱式類型轉換使用。比如:

int i;
float f = 166.7f;
= static_cast<int>(f);

此時結果,i的值為166。

2、C++中的reinterpret_cast主要是將數據從一種類型的轉換為另一種類型。所謂“通常為操作數的位模式提供較低層的重新解釋”也就是說將數據以二進制存在形式的重新解釋。比如:

int i;
char *= "This is a example.";
= reinterpret_cast<int>(p);

此時結果,i與p的值是完全相同的。reinterpret_cast的作用是說將指針p的值以二進制(位模式)的方式被解釋為整型,并賦給i,//i也是指針,整型指針;一個明顯的現象是在轉換前后沒有數位損失。


2 dynamic_cast < type-id > ( expression )用法:

該運算符把expression轉換成type-id類型的對象。Type-id必須是類的指針、類的引用或者void*;

如果type-id是類指針類型,那么expression也必須是一個指針,如果type-id是一個引用,那么expression也必須是一個引用。

dynamic_cast運算符可以在執行期決定真正的類型。如果downcast是安全的(也就說,如果基類指針或者引用確實指向一個派生類對象)這個運算符會傳回適當轉型過的指針。如果downcast不安全,這個運算符會傳回空指針(也就是說,基類指針或者引用沒有指向一個派生類對象)。

dynamic_cast主要用于類層次間的上行轉換和下行轉換,還可以用于類之間的交叉轉換。

在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是一樣的;

在進行下行轉換時,dynamic_cast具有類型檢查的功能,比static_cast更安全。

class B
{
public:
int m_iNum;
virtual void foo();
};

class D:public B
{
public:
char *m_szName[100];
};

void func(B *pb)
{
*pd1 = static_cast<*>(pb);
*pd2 = dynamic_cast<*>(pb);
}

在上面的代碼段中,如果pb指向一個D類型的對象,pd1和pd2是一樣的,并且對這兩個指針執行D類型的任何操作都是安全的;

但是,如果pb指向的是一個B類型的對象,那么pd1將是一個指向該對象的指針,對它進行D類型的操作將是不安全的(如訪問m_szName),而pd2將是一個空指針。B要有虛函數,否則會編譯出錯;static_cast則沒有這個限制。B中需要檢測有虛函數的原因:類中存在虛函數,就說明它有想要讓基類指針或引用指向派生類對象的情況,此時轉換才有意義。這是由于運行時類型檢查需要運行時類型信息,而這個信息存儲在類的虛函數表(關于虛函數表的概念,詳細可見<Inside c++ object model>)中,只有定義了虛函數的類才有虛函數表,

3 reinterpret_cast<type-id> (expression)用法:

該運算符的用法比較多。type-id必須是一個指針、引用、算術類型、函數指針或者成員指針。

它可以把一個指針轉換成一個整數,也可以把一個整數轉換成一個指針(先把一個指針轉換成一個整數,

在把該整數轉換成原類型的指針,還可以得到原先的指針值)。


4 const_cast<type_id> (expression)用法:

該運算符用來修改類型的const或volatile屬性。除了const 或volatile修飾之外, type_id和expression的類型是一樣的。

常量指針被轉化成非常量指針,并且仍然指向原來的對象;

常量引用被轉換成非常量引用,并且仍然指向原來的對象;常量對象被轉換成非常量對象。

舉如下一例:

class B
{
public:
    
int m_iNum;
}

void foo()
{
const B b1;
b1.m_iNum 
= 100;            //comile error
B b2 = const_cast<B>(b1);
b2. m_iNum 
= 200;           //fine
}

上面的代碼編譯時會報錯,因為b1是一個常量對象,不能對它進行改變;

使用const_cast把它轉換成一個常量對象,就可以對它的數據成員任意改變。注意:b1和b2是兩個不同的對象。



chenjt3533 2013-03-05 17:56 發表評論
]]>
vc++ 網絡編程http://www.shnenglu.com/chenjt3533/archive/2013/03/05/198226.htmlchenjt3533chenjt3533Tue, 05 Mar 2013 05:47:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/05/198226.htmlhttp://www.shnenglu.com/chenjt3533/comments/198226.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/05/198226.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198226.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198226.html在網絡編程中最常用的方案便是Client/Server (客戶機/服務器)模型。在這種方案中客戶應用程序向服務器程序請求服務。一個服務程序通常在一個眾所周知的地址監聽對服務的請求,也就是說,服務進程一直處于休眠狀態,直到一個客戶向這個服務的地址提出了連接請求。在這個時刻,服務程序被"驚醒"并且為客戶提供服務-對客戶的請求作出適當的反應。

  為了方便這種Client/Server模型的網絡編程,90年代初,由Microsoft聯合了其他幾家公司共同制定了一套WINDOWS下的網絡編程接口,即Windows Sockets規范,它不是一種網絡協議,而是一套開放的、支持多種協議的Windows下的網絡編程接口。現在的Winsock已經基本上實現了與協議無關,你可以使用Winsock來調用多種協議的功能,但較常使用的是TCP/IP協議。Socket實際在計算機中提供了一個通信端口,可以通過這個端口與任何一個具有Socket接口的計算機通信。應用程序在網絡上傳輸,接收的信息都通過這個Socket接口來實現。

  微軟為 Visual C++定義了Winsock類如CAsyncSocket類和派生于CAsyncSocket 的CSocket類,它們簡單易用,讀者朋友當然可以使用這些類來實現自己的網絡程序,但是為了更好的了解Winsock API編程技術,我們這里探討怎樣使用底層的API函數實現簡單的 Winsock 網絡應用程式設計,分別說明如何在Server端和Client端操作Socket,實現基于TCP/IP的數據傳送,最后給出相關的源代碼。

  在VC中進行WINSOCK的API編程開發的時候,需要在項目中使用下面的三個文件,否則會出現編譯錯誤。

  1.WINSOCK.H: 這是WINSOCK API的頭文件,需要包含在項目中。

  2.WSOCK32.LIB: WINSOCK API連接庫文件。在使用中,一定要把它作為項目的非缺省的連接庫包含到項目文件中去。 

  3.WINSOCK.DLL: WINSOCK的動態連接庫,位于WINDOWS的安裝目錄下。

  服務器端操作 socket(套接字)

  1.在初始化階段調用WSAStartup()

  此函數在應用程序中初始化Windows Sockets DLL ,只有此函數調用成功后,應用程序才可以再調用其他Windows Sockets DLL中的API函數。在程式中調用該函數的形式如下:WSAStartup((WORD)((1<<8|1),(LPWSADATA)&WSAData),其中(1<<8|1)表示我們用的是WinSocket1.1版本,WSAata用來存儲系統傳回的關于WinSocket的資料。

  2、建立Socket

  初始化WinSock的動態連接庫后,需要在服務器端建立一個監聽的Socket,為此可以調用Socket()函數用來建立這個監聽的Socket,并定義此Socket所使用的通信協議。此函數調用成功返回Socket對象,失敗則返回INVALID_SOCKET(調用WSAGetLastError()可得知原因,所有WinSocket 的API函數都可以使用這個函數來獲取失敗的原因)。

  SOCKET PASCAL FAR socket( int af, int type, int protocol )

  參數: af:目前只提供 PF_INET(AF_INET);

     type:Socket 的類型 (SOCK_STREAM、SOCK_DGRAM);

     protocol:通訊協定(如果使用者不指定則設為0);

  如果要建立的是遵從TCP/IP協議的socket,第二個參數type應為SOCK_STREAM,如為UDP(數據報)的socket,應為SOCK_DGRAM。

  3、綁定端口

  接下來要為服務器端定義的這個監聽的Socket指定一個地址及端口(Port),這樣客戶端才知道待會要連接哪一個地址的哪個端口,為此我們要調用bind()函數,該函數調用成功返回0,否則返回SOCKET_ERROR。

  int PASCAL FAR bind( SOCKET s, const struct sockaddr FAR *name,int namelen );

  參 數: s:Socket對象名;

      name:Socket的地址值,這個地址必須是執行這個程式所在機器的IP地址;

      namelen:name的長度; 

  如果使用者不在意地址或端口的值,那么可以設定地址為INADDR_ANY,及Port為0,Windows Sockets 會自動將其設定適當之地址及Port (1024 到 5000之間的值)。此后可以調用getsockname()函數來獲知其被設定的值。

  4、監聽

  當服務器端的Socket對象綁定完成之后,服務器端必須建立一個監聽的隊列來接收客戶端的連接請求。listen()函數使服務器端的Socket 進入監聽狀態,并設定可以建立的最大連接數(目前最大值限制為 5, 最小值為1)。該函數調用成功返回0,否則返回SOCKET_ERROR。

  int PASCAL FAR listen( SOCKET s, int backlog );

  參 數: s:需要建立監聽的Socket;

      backlog:最大連接個數;

  服務器端的Socket調用完listen()后,如果此時客戶端調用connect()函數提出連接申請的話,Server 端必須再調用accept() 函數,這樣服務器端和客戶端才算正式完成通信程序的連接動作。為了知道什么時候客戶端提出連接要求,從而服務器端的Socket在恰當的時候調用 accept()函數完成連接的建立,我們就要使用WSAAsyncSelect()函數,讓系統主動來通知我們有客戶端提出連接請求了。該函數調用成功返回0,否則返回SOCKET_ERROR。

  int PASCAL FAR WSAAsyncSelect( SOCKET s, HWND hWnd,unsigned int wMsg, long lEvent );

  參數: s:Socket 對象;
 
     hWnd :接收消息的窗口句柄;

     wMsg:傳給窗口的消息;

     lEvent:被注冊的網絡事件,也即是應用程序向窗口發送消息的網路事件,該值為下列值FD_READ、FD_WRITE、FD_OOB、 FD_ACCEPT、FD_CONNECT、FD_CLOSE的組合,各個值的具體含意為FD_READ:希望在套接字S收到數據時收到消息;FD_WRITE:希望在套接字S上可以發送數據時收到消息;FD_ACCEPT:希望在套接字S上收到連接請求時收到消息;FD_CONNECT:希望在套接字S上連接成功時收到消息;FD_CLOSE:希望在套接字S上連接關閉時收到消息;FD_OOB:希望在套接字S上收到帶外數據時收到消息。具體應用時,wMsg應是在應用程序中定義的消息名稱,而消息結構中的lParam則為以上各種網絡事件名稱。所以,可以在窗口處理自定義消息函數中使用以下結構來響應Socket的不同事件:  

switch(lParam) 
{
 case FD_READ:
   …  
   break;
 case FD_WRITE:
   …
   break;
 …
}


  5、服務器端接受客戶端的連接請求

  當Client提出連接請求時,Server 端hwnd視窗會收到Winsock Stack送來我們自定義的一個消息,這時,我們可以分析lParam,然后調用相關的函數來處理此事件。為了使服務器端接受客戶端的連接請求,就要使用 accept() 函數,該函數新建一Socket與客戶端的Socket相通,原先監聽之Socket繼續進入監聽狀態,等待他人的連接要求。該函數調用成功返回一個新產生的Socket對象,否則返回INVALID_SOCKET。

  SOCKET PASCAL FAR accept( SCOKET s, struct sockaddr FAR *addr,int FAR *addrlen );

  參數:s:Socket的識別碼;

     addr:存放來連接的客戶端的地址;

     addrlen:addr的長度

  6、結束 socket 連接

  結束服務器和客戶端的通信連接是很簡單的,這一過程可以由服務器或客戶機的任一端啟動,只要調用closesocket()就可以了,而要關閉 Server端監聽狀態的socket,同樣也是利用此函數。另外,與程序啟動時調用WSAStartup()憨數相對應,程式結束前,需要調用 WSACleanup() 來通知Winsock Dll釋放Socket所占用的資源。這兩個函數都是調用成功返回0,否則返回SOCKET_ERROR。

  int PASCAL FAR closesocket( SOCKET s );

  參數:s:Socket 的識別碼;

  int PASCAL FAR WSACleanup( void );

  參數: 無

(二)實現例子

服務器端:

#include <stdio.h>
#include 
<Winsock2.h>
#pragma comment(lib, 
"ws2_32.lib")
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested 
= MAKEWORD( 11 );

err 
= WSAStartup( wVersionRequested, &wsaData );
if ( err !=0 ) {
return;
}

if ( LOBYTE( wsaData.wVersion ) !=1||
HIBYTE( wsaData.wVersion ) 
!=1 ) {
WSACleanup( );
return;
}
SOCKET sockSrv
=socket(AF_INET,SOCK_STREAM,0);

SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr
=htonl(INADDR_ANY);
addrSrv.sin_family
=AF_INET;
addrSrv.sin_port
=htons(6000);

bind(sockSrv,(SOCKADDR
*)&addrSrv,sizeof(SOCKADDR));// 綁定端口

listen(sockSrv,
5);

SOCKADDR_IN addrClient;
// 連接上的客戶端ip地址
int len=sizeof(SOCKADDR);
while(1)
{
SOCKET sockConn
=accept(sockSrv,(SOCKADDR*)&addrClient,&len);// 接受客戶端連接,獲取客戶端的ip地址
char sendBuf[50];
sprintf(sendBuf,
"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));// 組合消息發送出去
send(sockConn,sendBuf,strlen(sendBuf)+1,0);// 發送消息到客戶端
char recvBuf[50];
recv(sockConn,recvBuf,
50,0);// 接受客戶端消息
printf("%s\n",recvBuf);
//closesocket(sockConn);//斷開連接
}

}

 客戶端代碼
#include <stdio.h>
#include 
<Winsock2.h>
#pragma comment(lib, 
"ws2_32.lib")
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
//WSAata用來存儲系統傳回的關于WinSocket的資料。
int err;

wVersionRequested 
= MAKEWORD( 11 );

err 
= WSAStartup( wVersionRequested, &wsaData );
if ( err !=0 ) {
return;
}

if ( LOBYTE( wsaData.wVersion ) !=1||HIBYTE( wsaData.wVersion ) !=1 ) 
{
WSACleanup( );
return;
}
SOCKET sockClient
=socket(AF_INET,SOCK_STREAM,0);// AF_INET ..tcp連接
//初始化連接與端口號
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr
=inet_addr("127.0.0.1");//本機地址,服務器在本機開啟
addrSrv.sin_family=AF_INET;
addrSrv.sin_port
=htons(6000);// 設置端口號
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//連接服務器
char recvBuf[50];
recv(sockClient,recvBuf,
50,0);//接受數據
printf("%s\n",recvBuf);
send(sockClient,
"hello",strlen("hello")+1,0);//發送數據
closesocket(sockClient);//關閉連接
WSACleanup();
}



chenjt3533 2013-03-05 13:47 發表評論
]]>
Vc++ 數據庫編程http://www.shnenglu.com/chenjt3533/archive/2013/03/05/198225.htmlchenjt3533chenjt3533Tue, 05 Mar 2013 05:41:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2013/03/05/198225.htmlhttp://www.shnenglu.com/chenjt3533/comments/198225.htmlhttp://www.shnenglu.com/chenjt3533/archive/2013/03/05/198225.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/198225.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/198225.htmlODBC開放數據庫互連(Open Database Connectivity)是微軟公司開放服務結構(WOSA,Windows Open Services Architecture)中有關數據庫的一個組成部分,它建立了一組規范,并提供了一組對數據庫訪問的標準API(應用程序編程接口)。這些API利用SQL來完成其大部分任務。ODBC本身也提供了對SQL語言的支持,用戶可以直接將SQL語句送給ODBC。

ADO (ActiveX Data Objects) 是微軟公司的一個用于存取數據源的COM組件。它提供了編程語言和統一數據訪問方式OLE DB的一個中間層。允許開發人員編寫訪問數據的代碼而不用關心數據庫是如何實現的,而只用關心到數據庫的連接。訪問數據庫的時候,關于SQL的知識不是必要的,但是特定數據庫支持的SQL命令仍可以通過ADO中的命令對象來執行。ADO被設計來繼承微軟早期的數據訪問對象層,包括RDO (Remote Data Objects) 和DAO(Data Access Objects)。

使用#import方法對ADO進行操作
在#import中,你需要提供所包含的類型庫的路徑和名稱,它能夠自動產生一個對GUIDs的定義,同時對自動生成對ADO對象的封裝。

還能夠列舉它在類型庫中所能找到的類型,對任何你所引用的類型庫,VC++會在編譯的時候自動生成兩個文件:
一個頭文件(.tlh),它包含了列舉的類型和對類型庫中對象的定義。
一個實現文件(.tli)對類型庫對象模型中的方法產生封裝。

#import "c:\Program Files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
/*VC++會自動產生msado15.tlh和msado15.tli兩個文件。no_namespace意味著你不需要在初始化變量的時候引用名字空間。對EOF進行該名,是必要的,因為典型的VC++應用都已經定義了EOF作為常數-1*/
       CoInitialize(NULL);
/*CoInitialize是 Windows提供的API函數,用來告訴 Windows以單線程的方式創建com對象。參數被保留,且必須為NULL。CoInitialize并不裝載COM 庫,它只用來初始化當前線程使用什么樣的套間。使用這個函數后,線程就和一個套間建立了對應關系,線程在此套間運行。CoInitialize和CoUninitialize必須成對使用。*/
          _ConnectionPtr m_pConnection(_uuidof(Connection));         
//使用智能指針產生一個連接指針
         _RecordsetPtr m_pRecordset(_uuidof(Recordset));            //使用智能指針產生一個記錄集指針
 try{
          m_pConnection
->Open("DSN=Student","","",0);          //建立連接,DSN(Data Source Name )是你要連接ODBC數據源的名稱
          m_pRecordset = m_pConnection->Execute("select * from Student",NULL,adCmdText);           //執行查詢語句
  while(!m_pRecordset->adoEOF)
  {
     _variant_t TheValue;   
//_variant_t封裝并管理VARIANT數據類型,是COM中使用的數據類型,COM是Component Object Model(組件對象模型)
     TheValue = m_pRecordset->GetCollect("Sname");         //獲取表中字段為“Sname”的值
     m_pRecordset->MoveNext();          //移動到下一條記錄
  }
 }
catch(_com_error e)
 {
      AfxMessageBox(e.ErrorMessage());
 }
     m_pRecordset
->Close();
     m_pConnection
->Close();
     m_pRecordset 
= NULL;
     m_pConnection 
= NULL;
     CoUninitialize(); 



chenjt3533 2013-03-05 13:41 發表評論
]]>
vs2008程序打包http://www.shnenglu.com/chenjt3533/archive/2012/11/16/195254.htmlchenjt3533chenjt3533Fri, 16 Nov 2012 01:28:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2012/11/16/195254.htmlhttp://www.shnenglu.com/chenjt3533/comments/195254.htmlhttp://www.shnenglu.com/chenjt3533/archive/2012/11/16/195254.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/195254.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/195254.html

一、創建程序項目(含有解決方案)

例如項目名為MyTest

二、創建安裝項目

在MyTest解決方案上“右擊”—>“添加”—>“新建項目”,選擇“其他類型項目”—>“安裝和部署”—>“安裝項目”,并命名為“ Install_MyTest ” 

三、添加項目需要文件(包括顯示在開始菜單中和桌面上快捷方式的圖標、卸載程序文件)

1、在右邊的“應用程序文件夾”上右擊“添加”—>“項目輸出”,選擇 MyTest 項目,這樣就相當于包含了所有 MyTest  的輸出文件;

1、在右邊的“應用程序文件夾”上右擊“添加”—>“文件”,選擇 MyTest 運行需要的所有文件(包括dll、lib、資源文件、配置文件)

3、在右邊的“應用程序文件夾”上右擊“添加”—>“文件”,選擇“c:"windows\system32\msiexec.exe”文件,用來制作卸載程序;

4、在右邊的“ 應用程序文件夾 ”上右擊“添加”—>“文件”,選擇兩個*.ico的圖標文件,一個作為啟動圖標、一個卸載圖標。

四、創建快捷方式(包括開始菜單快捷方式、桌面快捷方式、卸載快捷方式)

1、開始菜單快捷方式:在“主輸出來自 MyTest (活動)”上右擊“創建”主輸出來自 MyTest (活動)”的快捷方式“,命名為 MyTest ,并在其的屬性欄中為其“Icon”選擇剛才導入啟動的圖標。并將其拖動到“用戶的”程序”菜單”文件下。

2、桌面快捷方式:步驟同1

3、卸載快捷方式:在msiexec.exe上右擊“創建msiexec.exe 的快捷方式”,并命名為“卸載testwindows”。將其拖動到“用戶的”程序”菜單”文件下,當然也可放在桌面,將此快捷方式的Argmuments屬性設置為”/x {程序ID}”,ID值即為打包程序的ProductCode屬性,如“/x {1AE1E45C-C68B-4033-BE53-218FDEEF52D0}”(不包括雙引號)。

五、打包.net framework

選擇  Install_MyTest 項目的屬性,在對話框中選擇“系統必備”,然后在彈出的對話框中選擇“從與我的應用程序相同的位置下載系統必備組件”,確定。

六、生成

在 Install_MyTest 項目上右擊選擇“生成”,則打包成功,將在你的解決方案文件夾生成一個 Install_MyTest 的文件夾,安裝文件就在此目錄下。



chenjt3533 2012-11-16 09:28 發表評論
]]>
vs2008生成的各種文件http://www.shnenglu.com/chenjt3533/archive/2012/11/15/195213.htmlchenjt3533chenjt3533Thu, 15 Nov 2012 01:19:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2012/11/15/195213.htmlhttp://www.shnenglu.com/chenjt3533/comments/195213.htmlhttp://www.shnenglu.com/chenjt3533/archive/2012/11/15/195213.html#Feedback1http://www.shnenglu.com/chenjt3533/comments/commentRss/195213.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/195213.html
一、sln文件
    .sln(Solution)解決方案文件,表示一個項目組,他通常包含一個項目中所有的工程文件信息。

二、suo文件
    suo(Solution User Options)解決方案用戶選項文件,記錄所有與解決方案建立關聯的選項,以便在每次打開時,它都包含用戶所做的自定義設置。.suo文件偶爾會被破壞,從而在構建和編輯應用程序時出現意想不到的結果。如果Visual Studio對于每個解決方案不穩定,就應刪除.suo文件。下次打開解決方案時,Visual Studio會重建它。

三、vcproj文件
    vcproj(Visual Studio Project)vs工程文件,記錄工程中的所有文件信息。

四、obj文件
    .obj(Object)目標文件,程序編譯時生成的中間代碼文件,一般是程序編譯后的二進制文件,再通過鏈接器和資源文件鏈接就成exe文件了。OBJ只給出了程序的相對地址,而EXE是絕對地址。
五、pdb文件
    pdb(Program Debug Database)程序調試數據庫, 保存調試和項目狀態信息,從而可以對程序的調試配置進行增量鏈接。 
六、ncb文件
  NCB(No Compile Browser)無編譯瀏覽文件, NCB為VC++自動創建的跟蹤文件,其中存放了供ClassView、WizardBar和Component Gallery使用的信息,由VC開發環境自動生成。無編譯瀏覽文件。當自動完成功能出問題時可以刪除此文件。build編譯工程后會自動生成。

七、idb文件
    .idb()文件,MSDev中間層文件

八、pch文件
    .pch(Precompiled Header)編譯頭文件,是存放工程中預先編譯好的較穩定的代碼。編譯器是以文件為單位編譯,假設修改了一個文件就要對工程中所有文件重新編譯,肯定影響編譯效率。頭文件中所包括的東西往往非常大包括eg.Macro宏,Preprocessor預處理),編譯將占很長時間,但它們又不常被修改,是較穩定的,因此引入了.PCH文件。指定一個頭文件(.H),包含我們不會經常修改的代碼和其他的頭文件,然后用這個頭文件(.H)來生成一個預編譯頭文件(.PCH),VC默認的頭文件就是StdAfx.h,因為頭文件是不能編譯的,所以我們還需要一個.CPP文件來作橋梁,VC默認的文件為StdAfx.cpp,這個文件里只有一句代碼就是:#include "StdAfx.h"。接下來要用它生成.PCH文件,涉及到幾個重要的預編譯指令:/Yu,/Yc,/Yx,/Fp,/Yc是用來生成.PCH文件的編譯開關,在Project->setting->C/C++的Category里的Precompiled Header,然后在左邊的樹形視圖中選擇用來編譯生成.PCH文件的.CPP文件(默認即StdAfx.cpp)你就可以看到/Yc這個開關,它表示這個文件編譯了以后是否生成.PCH文件(可能/Yc的c表示create),/Fp指令指定生成的.PCH文件的名字及路徑(可能/Fp的p代表path),/Yu的u即use使用,工程中只要包括了.H文件的文件都會有這個/Yu指令,如果選擇自動Automatic...的話則原來為/Yc的地方就換成了/Yx指令,且每次編譯時編譯器會看以前有沒有生成過.PCH文件,有則不現生成否則就再次編譯產生.PCH文件.。

九、ilk文件
    鏈接臨時文件。


chenjt3533 2012-11-15 09:19 發表評論
]]>
DLL的創建與調用http://www.shnenglu.com/chenjt3533/archive/2012/11/14/195178.htmlchenjt3533chenjt3533Wed, 14 Nov 2012 01:35:00 GMThttp://www.shnenglu.com/chenjt3533/archive/2012/11/14/195178.htmlhttp://www.shnenglu.com/chenjt3533/comments/195178.htmlhttp://www.shnenglu.com/chenjt3533/archive/2012/11/14/195178.html#Feedback0http://www.shnenglu.com/chenjt3533/comments/commentRss/195178.htmlhttp://www.shnenglu.com/chenjt3533/services/trackbacks/195178.html

創建動態鏈接庫 (DLL) 項目:

1、從“文件”菜單中,選擇“新建”,然后選擇“項目…”。

2、在“項目類型”窗格中,選擇“Visual C++”下的“Win32”。

3、在“模板”窗格中,選擇“Win32 控制臺應用程序”。

4、為項目選擇一個名稱,如 MathFuncsDll,并將其鍵入“名稱”字段。 為解決方案選擇一個名稱,如 DynamicLibrary,并將其鍵入“解決方案名稱”字段。

5、單擊“確定”啟動 Win32 應用程序向導。 在“Win32 應用程序向導”對話框的“概述”頁中,單擊“下一步”。

6、在“Win32 應用程序向導”中的“應用程序設置”頁中,選擇“應用程序類型”下的“DLL”(如果可用),或者選擇“控制臺應用程序”(如果“DLL”不可用)。 某些版本的 Visual Studio 不支持通過使用向導創建 DLL 項目。 您可以稍后對此進行更改,以將項目編譯為 DLL。

7、在“Win32 應用程序向導”的“應用程序設置”頁中,選擇“附加選項”下的“空項目”。

8、單擊“完成”創建項目。

9、創建導出的類或函數等,必須在類或函數前加上 __declspec(dllexport) 修飾符。 這些修飾符使 DLL 能夠導出該類或函數以供其他應用程序使用。

調用動態鏈接庫 (DLL)

1、【C++】->【General】->【additional include directories】       //  添加頭文件目錄
2、【Linker】->【General】->【additional library directories】    //  添加lib目錄
3、【linker】->【input】->【additional dependencies】             //  添加lib文件名

將.dll可以放到工程生成的exe文件夾里面,運行exe就可以直接在當前目錄下找到需要的dll文件。(真正的函數的可執行代碼都在dll中,lib文件僅僅只是一個索引,而.h文件僅僅只是一個對外的接口) 
動態庫的三個組成部分
.h文件作用  : 聲明函數接口
.lib文件作用: 告訴鏈接器調用的函數在哪個DLL中
.dll文件作用: 函數可執行代碼
.h頭文件是編譯時必須的,lib是鏈接時需要的,dll是運行時需要的。完成源代碼的編譯和鏈接,有.h和.lib就夠了。要使動態連接的程序運行起來,有.dll就夠了。在開發和調試階段,當然最好都有。
當我們在自己的程序中引用了一個H文件里的函數,編鏈器怎么知道該調用哪個DLL文件呢?這就是LIB文件的作用: 告訴鏈接器 調用的函數在哪個DLL中,函數執行代碼在DLL中的什么位置,這也就是為什么需要附加依賴項 .LIB文件,它起到橋梁的作用。如果生成靜態庫文件,則沒有DLL ,只有lib,這時函數可執行代碼部分也在lib文件中
目前以lib后綴的庫有兩種,一種為靜態鏈接庫(Static Libary,以下簡稱“靜態庫”),另一種為動態連接庫(DLL,以下簡稱“動態庫”)的導入庫(Import Libary,以下簡稱“導入庫”)。靜態庫是一個或者多個obj文件的打包,所以有人干脆把從obj文件生成lib的過程稱為Archive,即合并到一起。比如你鏈接一個靜態庫,如果其中有錯,它會準確的找到是哪個obj有錯,即靜態lib只是殼子。動態庫一般會有對應的導入庫,方便程序靜態載入動態鏈接庫,否則你可能就需要自己LoadLibary調入DLL文件,然后再手工GetProcAddress獲得對應函數了。有了導入庫,你只需要鏈接導入庫后按照頭文件函數接口的聲明調用函數就可以了。導入庫和靜態庫的區別很大,他們實質是不一樣的東西。靜態庫本身就包含了實際執行代碼、符號表等等,而對于導入庫而言,其實際的執行代碼位于動態庫中,導入庫只包含了地址符號表等,確保程序找到對應函數的一些基本地址信息。


chenjt3533 2012-11-14 09:35 發表評論
]]>
亚洲国产精品无码久久久不卡| 久久无码国产专区精品| 久久中文字幕人妻丝袜| 久久国产综合精品五月天| 国产欧美久久一区二区| 久久天天躁狠狠躁夜夜躁2O2O| 7777精品伊人久久久大香线蕉 | 精品人妻伦九区久久AAA片69| 少妇人妻88久久中文字幕| 国产一区二区久久久| 欧美久久一区二区三区| 久久久青草青青国产亚洲免观| 国产成人久久精品麻豆一区 | 色婷婷久久综合中文久久蜜桃av| 国内精品久久久久影院老司 | 狠狠色丁香久久婷婷综合蜜芽五月| 亚洲v国产v天堂a无码久久| 中文字幕无码av激情不卡久久| 人妻无码久久精品| 久久久久久久97| 人妻无码久久一区二区三区免费 | 久久香蕉国产线看观看猫咪?v| 久久国产成人精品国产成人亚洲| 久久91这里精品国产2020| 久久夜色撩人精品国产小说| 久久久国产99久久国产一| 亚洲国产成人久久综合区| 精品综合久久久久久98| 久久久久亚洲AV片无码下载蜜桃| 精品久久无码中文字幕| 大香网伊人久久综合网2020| 久久婷婷色综合一区二区| 思思久久99热只有频精品66| 久久综合久久自在自线精品自 | 国产成人精品久久亚洲| 久久无码AV中文出轨人妻| 无码日韩人妻精品久久蜜桃| 99久久成人国产精品免费| 久久天天躁狠狠躁夜夜2020| 亚洲伊人久久大香线蕉综合图片| 久久99国产亚洲高清观看首页|