国产精品成av人在线视午夜片,欧美在线观看网址综合,91久久久久久久久http://m.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://m.shnenglu.com/chenjt3533/archive/2014/09/12/208285.htmlchenjt3533chenjt3533Fri, 12 Sep 2014 08:36:00 GMThttp://m.shnenglu.com/chenjt3533/archive/2014/09/12/208285.htmlhttp://m.shnenglu.com/chenjt3533/comments/208285.htmlhttp://m.shnenglu.com/chenjt3533/archive/2014/09/12/208285.html#Feedback0http://m.shnenglu.com/chenjt3533/comments/commentRss/208285.htmlhttp://m.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 發(fā)表評論
]]>
獲取農(nóng)歷年、時辰http://m.shnenglu.com/chenjt3533/archive/2014/08/26/208145.htmlchenjt3533chenjt3533Tue, 26 Aug 2014 15:28:00 GMThttp://m.shnenglu.com/chenjt3533/archive/2014/08/26/208145.htmlhttp://m.shnenglu.com/chenjt3533/comments/208145.htmlhttp://m.shnenglu.com/chenjt3533/archive/2014/08/26/208145.html#Feedback0http://m.shnenglu.com/chenjt3533/comments/commentRss/208145.htmlhttp://m.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 發(fā)表評論
]]>
ffplay 將視頻幀轉(zhuǎn)換成bmp圖片http://m.shnenglu.com/chenjt3533/archive/2014/08/21/208083.htmlchenjt3533chenjt3533Thu, 21 Aug 2014 14:21:00 GMThttp://m.shnenglu.com/chenjt3533/archive/2014/08/21/208083.htmlhttp://m.shnenglu.com/chenjt3533/comments/208083.htmlhttp://m.shnenglu.com/chenjt3533/archive/2014/08/21/208083.html#Feedback0http://m.shnenglu.com/chenjt3533/comments/commentRss/208083.htmlhttp://m.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);

        /// <轉(zhuǎn)換圖像格式>
        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 發(fā)表評論
]]>
c++ 獲取漢字首字母http://m.shnenglu.com/chenjt3533/archive/2013/05/31/200716.htmlchenjt3533chenjt3533Fri, 31 May 2013 05:01:00 GMThttp://m.shnenglu.com/chenjt3533/archive/2013/05/31/200716.htmlhttp://m.shnenglu.com/chenjt3533/comments/200716.htmlhttp://m.shnenglu.com/chenjt3533/archive/2013/05/31/200716.html#Feedback0http://m.shnenglu.com/chenjt3533/comments/commentRss/200716.htmlhttp://m.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)
{
    
/// 根據(jù)漢字區(qū)域碼獲取拼音聲母
    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 = "根據(jù)漢字區(qū)域碼獲取漢字首字母"// 輸入漢字

    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// 高字節(jié)
        wchr |= (sChinese[j++& 0xff);        // 低字節(jié)
        buff[i] = Convert(wchr);
    }


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








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

例:
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(); // 應(yīng)該使用this->Top() 或 MyStack<T>::Top(), 否則就調(diào)用外部Top(),或者編譯錯誤
 }

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


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


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

示T的靜態(tài)變量SubType乘以m_Ptr。
};

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

6、模板的申明和定義必須在同一個文檔中,否則會出現(xiàn)連接錯誤
例:
// 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、看看小細節(jié)
例:
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 發(fā)表評論
]]>
C++模板簡單分析與舉例http://m.shnenglu.com/chenjt3533/archive/2013/03/30/198947.htmlchenjt3533chenjt3533Sat, 30 Mar 2013 06:23:00 GMThttp://m.shnenglu.com/chenjt3533/archive/2013/03/30/198947.htmlhttp://m.shnenglu.com/chenjt3533/comments/198947.htmlhttp://m.shnenglu.com/chenjt3533/archive/2013/03/30/198947.html#Feedback0http://m.shnenglu.com/chenjt3533/comments/commentRss/198947.htmlhttp://m.shnenglu.com/chenjt3533/services/trackbacks/198947.html#pragma once#include <iostream>/**//*/////////////////////////////////////////////&nbs...  閱讀全文

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

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

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

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

 二:KMP算法
   剛才我們也說了,主串每次都要回溯,從而提高了時間復雜度,那么能不能在“主串”和“模式串”失配的情況下,主串不回溯呢?而是讓”模式串“向右滑動一定的距離,對上號后繼續(xù)進行下一輪的匹配,從而做到時間復雜度為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; // 計數(shù)

    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; // 計數(shù)

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

運行結(jié)果:


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


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

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

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

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


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

例子:
#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 發(fā)表評論
]]>
區(qū)分重載(overload),覆蓋(Override)和隱藏(hide)http://m.shnenglu.com/chenjt3533/archive/2013/03/08/198302.htmlchenjt3533chenjt3533Fri, 08 Mar 2013 09:29:00 GMThttp://m.shnenglu.com/chenjt3533/archive/2013/03/08/198302.htmlhttp://m.shnenglu.com/chenjt3533/comments/198302.htmlhttp://m.shnenglu.com/chenjt3533/archive/2013/03/08/198302.html#Feedback0http://m.shnenglu.com/chenjt3533/comments/commentRss/198302.htmlhttp://m.shnenglu.com/chenjt3533/services/trackbacks/198302.htmlhttp://blog.csdn.net/jixingzhong/article/details/1858943 

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


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

 

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



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

——每個現(xiàn)象后面都隱藏一個本質(zhì),關(guān)鍵在于我們是否去挖掘

寫在前面:

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

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

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

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

1、例子引入(現(xiàn)象)

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

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

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》

看下面的一個例子,來體會一下:實現(xiàn)一個打印函數(shù),既可以打印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
}

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

C  函數(shù)重載例子1

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

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

通過上面的介紹我們對函數(shù)重載,應(yīng)該喚醒了我們對函數(shù)重載的大概記憶。下面我們就來分析,C++是如何實現(xiàn)函數(shù)重載機制的。

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

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

發(fā)現(xiàn)函數(shù)void print(int i) 編譯之后為:(注意它的函數(shù)簽名變?yōu)?#8212;—_Z5printi

image

發(fā)現(xiàn)函數(shù)void print(string str) 編譯之后為:(注意它的函數(shù)簽名變?yōu)?#8212;—_Z5printSs

image

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

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

進一步猜想,前面的Z5表示返回值類型,print函數(shù)名,i表示整型int,Ss表示字符串string,即映射為返回類型+函數(shù)名+參數(shù)列表。最后在main函數(shù)中就是通過_Z5printi、_Z5printSs來調(diào)用對應(yīng)的函數(shù)的:

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

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

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

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

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

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

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

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

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

我們現(xiàn)在再來看一下這時print函數(shù)映射之后的函數(shù)名:

void print(int i)                    -->            _ZN4test5printEi

void print(char c)               -->            _ZN4test5printEc

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

3、重載函數(shù)的調(diào)用匹配

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

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

如果在最高層有多個匹配函數(shù)找到,調(diào)用將被拒絕(因為有歧義、模凌兩可)??聪旅娴睦樱?/p>


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

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

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

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

4、編譯器是如何解析重載函數(shù)調(diào)用的?

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

image 

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

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

 

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

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

4.1、根據(jù)函數(shù)名確定候選函數(shù)集

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

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

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

為了查找候選函數(shù)集,一般采用深度優(yōu)選搜索算法:

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

在收集候選函數(shù)時,如果調(diào)用函數(shù)的實參類型為非結(jié)構(gòu)體類型,候選函數(shù)僅包含調(diào)用點可見的函數(shù);如果調(diào)用函數(shù)的實參類型包括類類型對象、類類型指針、類類型引用或指向類成員的指針,候選函數(shù)為下面集合的并:

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

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

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

 

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

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

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

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

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

4.2、確定可用函數(shù)

可用的函數(shù)是指:函數(shù)參數(shù)個數(shù)匹配并且每一個參數(shù)都有隱式轉(zhuǎn)換序列。

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

這些規(guī)則在前面的《3、重載函數(shù)的調(diào)用匹配》中就有所體現(xiàn)了。

4.3、確定最佳匹配函數(shù)

確定可用函數(shù)之后,對可用函數(shù)集中的每一個函數(shù),如果調(diào)用函數(shù)的實參要調(diào)用它計算優(yōu)先級,最后選出優(yōu)先級最高的。如對《3、重載函數(shù)的調(diào)用匹配》中介紹的匹配規(guī)則中按順序分配權(quán)重,然后計算總的優(yōu)先級,最后選出最優(yōu)的函數(shù)。

 

5、總結(jié)

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

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

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

附錄:一種C++函數(shù)重載機制

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

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

image

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

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

image

圖附2、過程2- 重載函數(shù)調(diào)用,查找鏈表

附-模板函數(shù)和普通函數(shù)構(gòu)成的重載,調(diào)用時又是如何匹配的呢?

下面是C++創(chuàng)始人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 發(fā)表評論
]]>
C++操作符重載http://m.shnenglu.com/chenjt3533/archive/2013/03/07/198256.htmlchenjt3533chenjt3533Thu, 07 Mar 2013 03:07:00 GMThttp://m.shnenglu.com/chenjt3533/archive/2013/03/07/198256.htmlhttp://m.shnenglu.com/chenjt3533/comments/198256.htmlhttp://m.shnenglu.com/chenjt3533/archive/2013/03/07/198256.html#Feedback0http://m.shnenglu.com/chenjt3533/comments/commentRss/198256.htmlhttp://m.shnenglu.com/chenjt3533/services/trackbacks/198256.html
//原文鏈接: http://blog.csdn.net/xiegenwendada/article/details/8477209 

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

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

// 簡單的重載
class CBaseOperator  
{  

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

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


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

public:  

    //重載+操作符,簡單的二元操作符重載是最常見也是比較簡單的?;舅悸范际沁@樣,注意如果  
    
//操作符出現(xiàn)在左邊,則只能用友員了。這里了有幾個返回類型是CBaseOperator,return  
    
//語句中卻是兩個int相加,這種情況是可以通過的,編譯器會自動構(gòu)建一個相應(yīng)的對象返回,  
    
//前提是你的構(gòu)造函數(shù)要有相應(yīng)的參數(shù),這里是int作為參數(shù)  
    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;  
    }  

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

    
//比較操作符重載==,!=,>, >=, <, <=總結(jié):這里都是配套的操作一般來說如果寫一個  
    
//都會重載其他幾個,特別是==,!=。當然也有例外。哈哈。。  
    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先改變自己,返回),  
    
//一種是后增量(改變自己,返回改變前的狀態(tài))  
    
//因為重載是判斷參數(shù)的,為了能區(qū)別前增量和后增量,C++的設(shè)計者做了這樣的考慮。  
    
//就是重載后增量的時候在參數(shù)列表中加一個int類型參數(shù),這樣就避免的重載的重復了。  
    
//在調(diào)用上,如果都重載,那么用int參數(shù)的是后增量++,沒有參數(shù)的是前增量++,  
    
//(注:我在這里說的是成員函數(shù),當然友員的重載參數(shù)個數(shù)要多一個,以后的我可別說我無知啊。)  
    
//如果都重載,那么前增量和后增量都會調(diào)用相應(yīng)的函數(shù),如果只重載了后增量,那么前增量會失敗  
    
//如果只重載了前增量,就會無論是前增量和后增量都會調(diào)用這個函數(shù)。所以一般他們也是成對  
    
//出現(xiàn)的,除非你懶,只寫前增量,可惜如果人家要調(diào)用后增量呢?結(jié)果會錯的哦。哈哈。  

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

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

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

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

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

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

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

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

    
//重載new delete,這里編譯器做了一個限制,new必須返回void*類型, delete必須  
    
//返回void類型。(上面說過函數(shù)重載是不檢查返回類型的,和這里并沒有沖突,他只是限定了返回  
    
//類型,而不是只有返回類型不同的函數(shù)能重載,這個是編譯器做的工作,一定上確保new能更好的工作吧)  
    
//還有就是他們的參數(shù)個數(shù)都是可以自定義的。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);  

    //這里順便檢測一下拷貝構(gòu)造函數(shù)  
    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 發(fā)表評論
]]>
C++四種強制轉(zhuǎn)換http://m.shnenglu.com/chenjt3533/archive/2013/03/05/198235.htmlchenjt3533chenjt3533Tue, 05 Mar 2013 09:56:00 GMThttp://m.shnenglu.com/chenjt3533/archive/2013/03/05/198235.htmlhttp://m.shnenglu.com/chenjt3533/comments/198235.htmlhttp://m.shnenglu.com/chenjt3533/archive/2013/03/05/198235.html#Feedback0http://m.shnenglu.com/chenjt3533/comments/commentRss/198235.htmlhttp://m.shnenglu.com/chenjt3533/services/trackbacks/198235.html1 static_cast < type-id > ( expression )用法:

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

①用于類層次結(jié)構(gòu)中基類(父類)和派生類(子類)之間指針或引用的轉(zhuǎn)換。

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

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

②用于基本數(shù)據(jù)類型之間的轉(zhuǎn)換,如把int轉(zhuǎn)換成char,把int轉(zhuǎn)換成enum。這種轉(zhuǎn)換的安全性也要開發(fā)人員來保證。

③把空指針轉(zhuǎn)換成目標類型的空指針。

④把任何類型的表達式轉(zhuǎn)換成void類型。

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

C++中static_cast和reinterpret_cast的區(qū)別

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

1、C++中的static_cast執(zhí)行非多態(tài)的轉(zhuǎn)換,用于代替C中通常的轉(zhuǎn)換操作。因此,被做為隱式類型轉(zhuǎn)換使用。比如:

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

此時結(jié)果,i的值為166。

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

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

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


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

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

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

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

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

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

在進行下行轉(zhuǎn)換時,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是一樣的,并且對這兩個指針執(zhí)行D類型的任何操作都是安全的;

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

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

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

它可以把一個指針轉(zhuǎn)換成一個整數(shù),也可以把一個整數(shù)轉(zhuǎn)換成一個指針(先把一個指針轉(zhuǎn)換成一個整數(shù),

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


4 const_cast<type_id> (expression)用法:

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

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

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

舉如下一例:

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把它轉(zhuǎn)換成一個常量對象,就可以對它的數(shù)據(jù)成員任意改變。注意:b1和b2是兩個不同的對象。



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

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

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

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

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

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

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

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

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

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

  2、建立Socket

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

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

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

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

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

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

  3、綁定端口

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

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

  參 數(shù): s:Socket對象名;

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

      namelen:name的長度; 

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

  4、監(jiān)聽

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

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

  參 數(shù): s:需要建立監(jiān)聽的Socket;

      backlog:最大連接個數(shù);

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

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

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

     wMsg:傳給窗口的消息;

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

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


  5、服務(wù)器端接受客戶端的連接請求

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

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

  參數(shù):s:Socket的識別碼;

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

     addrlen:addr的長度

  6、結(jié)束 socket 連接

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

  int PASCAL FAR closesocket( SOCKET s );

  參數(shù):s:Socket 的識別碼;

  int PASCAL FAR WSACleanup( void );

  參數(shù): 無

(二)實現(xiàn)例子

服務(wù)器端:

#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));// 組合消息發(fā)送出去
send(sockConn,sendBuf,strlen(sendBuf)+1,0);// 發(fā)送消息到客戶端
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用來存儲系統(tǒng)傳回的關(guān)于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");//本機地址,服務(wù)器在本機開啟
addrSrv.sin_family=AF_INET;
addrSrv.sin_port
=htons(6000);// 設(shè)置端口號
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//連接服務(wù)器
char recvBuf[50];
recv(sockClient,recvBuf,
50,0);//接受數(shù)據(jù)
printf("%s\n",recvBuf);
send(sockClient,
"hello",strlen("hello")+1,0);//發(fā)送數(shù)據(jù)
closesocket(sockClient);//關(guān)閉連接
WSACleanup();
}



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

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

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

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

#import "c:\Program Files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
/*VC++會自動產(chǎn)生msado15.tlh和msado15.tli兩個文件。no_namespace意味著你不需要在初始化變量的時候引用名字空間。對EOF進行該名,是必要的,因為典型的VC++應(yīng)用都已經(jīng)定義了EOF作為常數(shù)-1*/
       CoInitialize(NULL);
/*CoInitialize是 Windows提供的API函數(shù),用來告訴 Windows以單線程的方式創(chuàng)建com對象。參數(shù)被保留,且必須為NULL。CoInitialize并不裝載COM 庫,它只用來初始化當前線程使用什么樣的套間。使用這個函數(shù)后,線程就和一個套間建立了對應(yīng)關(guān)系,線程在此套間運行。CoInitialize和CoUninitialize必須成對使用。*/
          _ConnectionPtr m_pConnection(_uuidof(Connection));         
//使用智能指針產(chǎn)生一個連接指針
         _RecordsetPtr m_pRecordset(_uuidof(Recordset));            //使用智能指針產(chǎn)生一個記錄集指針
 try{
          m_pConnection
->Open("DSN=Student","","",0);          //建立連接,DSN(Data Source Name )是你要連接ODBC數(shù)據(jù)源的名稱
          m_pRecordset = m_pConnection->Execute("select * from Student",NULL,adCmdText);           //執(zhí)行查詢語句
  while(!m_pRecordset->adoEOF)
  {
     _variant_t TheValue;   
//_variant_t封裝并管理VARIANT數(shù)據(jù)類型,是COM中使用的數(shù)據(jù)類型,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 發(fā)表評論
]]>
vs2008程序打包http://m.shnenglu.com/chenjt3533/archive/2012/11/16/195254.htmlchenjt3533chenjt3533Fri, 16 Nov 2012 01:28:00 GMThttp://m.shnenglu.com/chenjt3533/archive/2012/11/16/195254.htmlhttp://m.shnenglu.com/chenjt3533/comments/195254.htmlhttp://m.shnenglu.com/chenjt3533/archive/2012/11/16/195254.html#Feedback0http://m.shnenglu.com/chenjt3533/comments/commentRss/195254.htmlhttp://m.shnenglu.com/chenjt3533/services/trackbacks/195254.html

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

例如項目名為MyTest

二、創(chuàng)建安裝項目

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

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

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

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

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

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

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

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

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

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

五、打包.net framework

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

六、生成

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



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

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

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

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

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

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

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


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

創(chuàng)建動態(tài)鏈接庫 (DLL) 項目:

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

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

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

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

5、單擊“確定”啟動 Win32 應(yīng)用程序向?qū)А?在“Win32 應(yīng)用程序向?qū)?#8221;對話框的“概述”頁中,單擊“下一步”。

6、在“Win32 應(yīng)用程序向?qū)?#8221;中的“應(yīng)用程序設(shè)置”頁中,選擇“應(yīng)用程序類型”下的“DLL”(如果可用),或者選擇“控制臺應(yīng)用程序”(如果“DLL”不可用)。 某些版本的 Visual Studio 不支持通過使用向?qū)?chuàng)建 DLL 項目。 您可以稍后對此進行更改,以將項目編譯為 DLL。

7、在“Win32 應(yīng)用程序向?qū)?#8221;的“應(yīng)用程序設(shè)置”頁中,選擇“附加選項”下的“空項目”。

8、單擊“完成”創(chuàng)建項目。

9、創(chuàng)建導出的類或函數(shù)等,必須在類或函數(shù)前加上 __declspec(dllexport) 修飾符。 這些修飾符使 DLL 能夠?qū)С鲈擃惢蚝瘮?shù)以供其他應(yīng)用程序使用。

調(diào)用動態(tài)鏈接庫 (DLL)

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

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


chenjt3533 2012-11-14 09:35 發(fā)表評論
]]>
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            一区视频在线看| 亚洲精选一区二区| 亚洲国产精品久久91精品| 国产日韩一区欧美| 国产在线精品自拍| 国产一区二区成人| 韩日精品视频一区| 亚洲激情av在线| 伊人久久亚洲影院| 亚洲肉体裸体xxxx137| 99精品国产在热久久婷婷| 亚洲综合成人在线| 久久精品国内一区二区三区| 老司机免费视频一区二区三区| 欧美a级片网站| 一区二区高清在线观看| 欧美一区激情| 欧美日韩www| 国产欧美在线| 亚洲精品国产无天堂网2021| 亚洲欧美国产毛片在线| 久久夜色精品国产欧美乱极品| 亚洲国产成人精品久久| 亚洲黑丝一区二区| 欧美在线视频导航| 欧美激情一区二区三区全黄| 国产乱码精品1区2区3区| 亚洲国产精品成人一区二区 | 欧美午夜美女看片| 国产自产高清不卡| 亚洲色图自拍| 欧美成人中文字幕| 亚洲尤物影院| 欧美日韩国产成人高清视频| 激情五月***国产精品| 亚洲欧美日本国产有色| 欧美日韩国产精品一区二区亚洲| 亚洲电影在线观看| 亚洲欧美变态国产另类| 免费欧美电影| 国产偷久久久精品专区| 宅男精品视频| 亚洲福利小视频| 久久精精品视频| 国产精品久久久久久久久婷婷 | 国产精品区一区二区三| 亚洲美女网站| 欧美α欧美αv大片| 亚洲欧美在线视频观看| 欧美色网一区二区| 99re热精品| 亚洲第一免费播放区| 午夜综合激情| 国产精品视频免费观看| 一本一本久久a久久精品综合妖精 一本一本久久a久久精品综合麻豆 | 国产精品综合网站| 亚洲一区二区三区中文字幕| 亚洲国产精品久久91精品| 久久人人爽国产| 国内精品免费午夜毛片| 久久九九热免费视频| 性欧美精品高清| 国产亚洲精品bv在线观看| 亚洲嫩草精品久久| 亚洲自拍偷拍网址| 国产欧美精品日韩| 久久精品一本久久99精品| 午夜亚洲伦理| 国产专区综合网| 美女免费视频一区| 美女网站在线免费欧美精品| 亚洲国产精品999| 欧美激情中文不卡| 欧美韩日高清| 在线午夜精品| 亚洲综合成人在线| 国精产品99永久一区一区| 免费成人小视频| 欧美激情精品久久久久| 亚洲欧美日韩国产综合精品二区| 一区二区三区欧美亚洲| 国产欧美精品久久| 欧美va亚洲va国产综合| 欧美激情自拍| 欧美亚洲在线观看| 午夜日本精品| 欧美黄在线观看| 欧美日韩午夜在线| 亚洲高清视频中文字幕| 亚洲一级片在线看| 欧美日韩国产123| 午夜精品久久久久影视| 欧美一区视频| 一个人看的www久久| 亚洲欧美日韩国产综合| 激情欧美国产欧美| 一本久道久久综合婷婷鲸鱼| 国产欧美日韩亚洲| 亚洲国产综合视频在线观看| 国产精品区一区二区三| 欧美激情中文不卡| 国产日韩欧美a| 亚洲日本国产| 激情文学综合丁香| 亚洲一区视频在线观看视频| 国产精品自在线| 亚洲国产三级| 一区在线观看视频| 在线亚洲美日韩| 亚洲精品欧洲精品| 久久精品夜色噜噜亚洲a∨ | 亚洲午夜视频| 亚洲三级色网| 久久久精品欧美丰满| 亚洲欧美日韩一区二区| 噜噜噜91成人网| 久久久99国产精品免费| 欧美日韩爆操| 亚洲二区在线观看| 国产综合精品| 午夜视黄欧洲亚洲| 亚洲自啪免费| 欧美日韩一区二区三区在线 | 欧美电影免费观看大全| 国产亚洲成人一区| 亚洲天堂久久| 亚洲一区二区三区四区五区午夜| 久热精品视频在线| 久久综合狠狠| 黄色一区二区在线| 欧美在线免费一级片| 久久国产精品久久久久久电车| 欧美午夜精品久久久久久浪潮| 亚洲国产一区二区三区a毛片| 亚洲电影在线免费观看| 久久免费精品视频| 美腿丝袜亚洲色图| 一区在线观看| 蜜桃av一区二区三区| 欧美电影在线免费观看网站| 精品二区久久| 免费成人性网站| 亚洲欧洲一区二区三区| 一本色道久久综合亚洲精品不卡| 欧美激情精品久久久久久变态| 欧美黄色免费网站| 日韩视频不卡中文| 亚洲电影免费在线| 久久精品视频在线看| 久久只精品国产| 亚洲第一页中文字幕| 开元免费观看欧美电视剧网站| 欧美肥婆在线| 一本一本久久a久久精品综合妖精 一本一本久久a久久精品综合麻豆 | 日韩午夜在线| 欧美日韩亚洲综合| 一区二区三区四区国产| 性18欧美另类| 樱花yy私人影院亚洲| 欧美激情一区在线观看| 亚洲线精品一区二区三区八戒| 欧美与黑人午夜性猛交久久久| 国产一在线精品一区在线观看| 久久久免费av| 亚洲毛片一区| 久久er99精品| 亚洲国产婷婷香蕉久久久久久| 欧美巨乳在线观看| 亚洲亚洲精品在线观看| 久久久在线视频| 亚洲乱码国产乱码精品精98午夜 | 久久久噜噜噜久久人人看| 国产精品大片wwwwww| 亚洲免费一级电影| 免费看av成人| 亚洲一区二区三区四区视频| 欧美四级在线| 欧美有码视频| 一区二区三区波多野结衣在线观看| 久久久久国产精品www | 久久国产高清| 亚洲电影专区| 国产精品乱码人人做人人爱| 午夜精品久久| 亚洲美女视频| 欧美 日韩 国产 一区| 国产精品99久久不卡二区| 国产一区二区三区电影在线观看| 欧美成人精品一区二区| 欧美一区二区三区日韩| 999亚洲国产精| 欧美国产日韩二区| 久久精品国产欧美亚洲人人爽| 一区二区三区欧美| 亚洲激情视频在线观看| 国产日韩在线播放| 国产精品久久久久9999高清| 欧美黑人国产人伦爽爽爽| 久久免费视频网| 久久www成人_看片免费不卡|