欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++中的變長(zhǎng)參數(shù)深入理解

 更新時(shí)間:2016年10月19日 15:20:24   作者:kevonyang  
變長(zhǎng)參數(shù)的函數(shù),即參數(shù)個(gè)數(shù)可變、參數(shù)類型不定的函數(shù)。設(shè)計(jì)一個(gè)參數(shù)個(gè)數(shù)可變、參數(shù)類型不定的函數(shù)是可能的,最常見(jiàn)的例子是printf函數(shù)、scanf函數(shù)和高級(jí)語(yǔ)言的Format函數(shù)。最近的一個(gè)項(xiàng)目中就遇到這么一個(gè)相關(guān)的問(wèn)題,感興趣的朋友們下面來(lái)一起看看吧。

前言

在吸進(jìn)的一個(gè)項(xiàng)目中為了使用共享內(nèi)存和自定義內(nèi)存池,我們自己定義了MemNew函數(shù),且在函數(shù)內(nèi)部對(duì)于非pod類型自動(dòng)執(zhí)行構(gòu)造函數(shù)。在需要的地方調(diào)用自定義的MemNew函數(shù)。這樣就帶來(lái)一個(gè)問(wèn)題,使用stl的類都有默認(rèn)構(gòu)造函數(shù),以及復(fù)制構(gòu)造函數(shù)等。但使用共享內(nèi)存和內(nèi)存池的類可能沒(méi)有默認(rèn)構(gòu)造函數(shù),而是定義了多個(gè)參數(shù)的構(gòu)造函數(shù),于是如何將參數(shù)傳入MemNew函數(shù)便成了問(wèn)題。

一、變長(zhǎng)參數(shù)函數(shù)

首先回顧一下較多使用的變長(zhǎng)參數(shù)函數(shù),最經(jīng)典的便是printf。

extern int printf(const char *format, ...);

以上是一個(gè)變長(zhǎng)參數(shù)的函數(shù)聲明。我們自己定義一個(gè)測(cè)試函數(shù):

#include <stdarg.h>
#include <stdio.h>

int testparams(int count, ...)
{
 va_list args;
 va_start(args, count);
 for (int i = 0; i < count; ++i)
 {
  int arg = va_arg(args, int);
  printf("arg %d = %d", i, arg);
 }
 va_end(args);
 return 0;
}

int main()
{
 testparams(3, 10, 11, 12);
 return 0;
}

變長(zhǎng)參數(shù)函數(shù)的解析,使用到三個(gè)宏va_start,va_arg va_end,再看va_list的定義 typedef char* va_list; 只是一個(gè)char指針。

這幾個(gè)宏如何解析傳入的參數(shù)呢?

函數(shù)的調(diào)用,是一個(gè)壓棧,保存,跳轉(zhuǎn)的過(guò)程。

簡(jiǎn)單的流程描述如下:

      1、把參數(shù)從右到左依次壓入棧;

      2、調(diào)用call指令,把下一條要執(zhí)行的指令的地址作為返回地址入棧;(被調(diào)用函數(shù)執(zhí)行完后會(huì)回到該地址繼續(xù)執(zhí)行)

      3、當(dāng)前的ebp(基址指針)入棧保存,然后把當(dāng)前esp(棧頂指針)賦給ebp作為新函數(shù)棧幀的基址;

      4、執(zhí)行被調(diào)用函數(shù),局部變量等入棧;

      5、返回值放入eax,leave,ebp賦給esp,esp所存的地址賦給ebp;(這里可能需要拷貝臨時(shí)返回對(duì)象)
從返回地址開始繼續(xù)執(zhí)行;(把返回地址所存的地址給eip)

由于開始的時(shí)候從右至左把參數(shù)壓棧,va_start 傳入最左側(cè)的參數(shù),往右的參數(shù)依次更早被壓入棧,因此地址依次遞增(棧頂?shù)刂纷钚。?code>va_arg傳入當(dāng)前需要獲得的參數(shù)的類型,便可以利用 sizeof 計(jì)算偏移量,依次獲取后面的參數(shù)值。

#define _INTSIZEOF(n)   ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))

#define _ADDRESSOF(v) (&const_cast<char&>(reinterpret_cast<const volatile char&>(v)))

#define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
#define __crt_va_arg(ap, t)  (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define __crt_va_end(ap)  ((void)(ap = (va_list)0))

#define __crt_va_start(ap, x) ((void)(__vcrt_va_start_verify_argument_type<decltype(x)>(), __crt_va_start_a(ap, x)))

#define va_start __crt_va_start
#define va_arg __crt_va_arg
#define va_end __crt_va_end

上述宏定義中, _INTSIZEOF(n) 將地址的低2位指令,做內(nèi)存的4字節(jié)對(duì)齊。每次取參數(shù)時(shí),調(diào)用__crt_va_arg(ap,t)  ,返回t類型參數(shù)地址的值,同時(shí)將ap偏移到t之后。最后,調(diào)用_crt_va_end(ap)將ap置0.

變長(zhǎng)參數(shù)的函數(shù)的使用及其原理看了宏定義是很好理解的。從上文可知,要使用變長(zhǎng)參數(shù)函數(shù)的參數(shù),我們必須知道傳入的每個(gè)參數(shù)的類型。printf中,有format字符串中的特殊字符組合來(lái)解析后面的參數(shù)類型。但是當(dāng)傳入類的構(gòu)造函數(shù)的參數(shù)時(shí),我們并不知道每個(gè)參數(shù)都是什么類型,雖然參數(shù)能夠依次傳入函數(shù),但無(wú)法解析并獲取每個(gè)參數(shù)的數(shù)值。因此傳統(tǒng)的變長(zhǎng)參數(shù)函數(shù)并不足以解決傳入任意構(gòu)造函數(shù)參數(shù)的問(wèn)題。

二、變長(zhǎng)參數(shù)模板

我們需要用到C++11的新特性,變長(zhǎng)參數(shù)模板。

這里舉一個(gè)使用自定義內(nèi)存池的例子。定義一個(gè)內(nèi)存池類MemPool.h,以count個(gè)類型T為單元分配內(nèi)存,默認(rèn)分配一個(gè)對(duì)象。每當(dāng)內(nèi)存內(nèi)空閑內(nèi)存不夠,則一次申請(qǐng)MEMPOOL_NEW_SIZE個(gè)內(nèi)存對(duì)象。內(nèi)存池本身只負(fù)責(zé)內(nèi)存分配,不做初始化工作,因此不需要傳入任何參數(shù),只需實(shí)例化模板分配相應(yīng)類型的內(nèi)存即可。

#ifndef UTIL_MEMPOOL_H
#define UTIL_MEMPOOL_H

#include <stdlib.h>

#define MEMPOOL_NEW_SIZE 8

template<typename T, size_t count = 1>
class MemPool
{
private:
 union MemObj {
  char _obj[1];
  MemObj* _freelink;
 };

public:
 static void* Allocate()
 {
  if (!_freelist) {
   refill();
  }
  MemObj* alloc_mem = _freelist;
  _freelist = _freelist->_freelink;
  ++_size;
  return (void*)alloc_mem;
 }

 static void DeAllocate(void* p)
 {
  MemObj* q = (MemObj*)p;
  q->_freelink = _freelist;
  _freelist = q;
  --_size;
 }

 static size_t TotalSize() {
  return _totalsize;
 }

 static size_t Size() {
  return _size;
 }
private:
 static void refill()
 {
  size_t size = sizeof(T) * count;
  char* new_mem = (char*)malloc(size * MEMPOOL_NEW_SIZE);
  for (int i = 0; i < MEMPOOL_NEW_SIZE; ++i) {
   MemObj* free_mem = (MemObj*)(new_mem + i * size);
   free_mem->_freelink = _freelist;
   _freelist = free_mem;
  }
  _totalsize += MEMPOOL_NEW_SIZE;
 }

 static MemObj* _freelist;
 static size_t _totalsize;
 static size_t _size;
};

template<typename T, size_t count>
typename MemPool<T, count>::MemObj* MemPool<T, count>::_freelist = NULL;

template<typename T, size_t count>
size_t MemPool<T, count>::_totalsize = 0;

template<typename T, size_t count>
size_t MemPool<T, count>::_size = 0;
#endif

接下來(lái)在沒(méi)有變長(zhǎng)參數(shù)的情況下,實(shí)現(xiàn)通用MemNewMemDelete函數(shù)模板。這里不對(duì)函數(shù)模板作詳細(xì)解釋,用函數(shù)模板我們可以對(duì)不同的類型實(shí)現(xiàn)同樣的內(nèi)存池分配操作。如下:

template<class T>
T *MemNew(size_t count)
{
 T *p = (T*)MemPool<T, count>::Allocate();
 if (p != NULL)
 {
  if (!std::is_pod<T>::value)
  {
   for (size_t i = 0; i < count; ++i)
   {
    new (&p[i]) T();
   }
  }
 }
 return p;
}

template<class T>
T *MemDelete(T *p, size_t count)
{
 if (p != NULL)
 {
  if (!std::is_pod<T>::value)
  {
   for (size_t i = 0; i < count; ++i)
   {
    p[i].~T();
   }
  }
  MemPool<T, count>::DeAllocate(p);
 }
}

上述實(shí)現(xiàn)中,使用placement new對(duì)申請(qǐng)的內(nèi)存進(jìn)行構(gòu)造,使用了默認(rèn)構(gòu)造函數(shù),當(dāng)申請(qǐng)內(nèi)存的類型不具備默認(rèn)構(gòu)造函數(shù)時(shí),placement new將報(bào)錯(cuò)。對(duì)于pod類型,可以省去調(diào)用構(gòu)造函數(shù)的過(guò)程。

引入C++11變長(zhǎng)模板參數(shù)后MemNew修改為如下

template<class T, class... Args>
T *MemNew(size_t count, Args&&... args)
{
 T *p = (T*)MemPool<T, count>::Allocate();
 if (p != NULL)
 {
  if (!std::is_pod<T>::value)
  {
   for (size_t i = 0; i < count; ++i)
   {
    new (&p[i]) T(std::forward<Args>(args)...);
   }
  }
 }
 return p;
}

以上函數(shù)定義包含了多個(gè)特性,后面我將一一解釋,其中class... Args 表示變長(zhǎng)參數(shù)模板,函數(shù)參數(shù)中Args&& 為右值引用。std::forward<Args> 實(shí)現(xiàn)參數(shù)的完美轉(zhuǎn)發(fā)。這樣,無(wú)論傳入的類型具有什么樣的構(gòu)造函數(shù),都能夠完美執(zhí)行

C++11中引入了變長(zhǎng)參數(shù)模板的概念,來(lái)解決參數(shù)個(gè)數(shù)不確定的模板。

template<class... T> class Test {};
Test<> test0;
Test<int> test1;
Test<int,int> test2;
Test<int,int,long> test3;

template<class... T> void test(T... args);
test();
test<int>(0);
test<int,int,long>(0,0,0L);

以上分別是使用變長(zhǎng)參數(shù)類模板和變長(zhǎng)參數(shù)函數(shù)模板的例子。

2.1變長(zhǎng)參數(shù)函數(shù)模板

T... args 為形參包,其中args是模式,形參包中可以有0到任意多個(gè)參數(shù)。調(diào)用函數(shù)時(shí),可以傳任意多個(gè)實(shí)參。對(duì)于函數(shù)定義來(lái)說(shuō),該如何使用參數(shù)包呢?在上文的MemNew中,我們使用std::forward依次將參數(shù)包傳入構(gòu)造函數(shù),并不關(guān)注每個(gè)參數(shù)具體是什么。如果需要,我們可以用sizeof...(args)操作獲取參數(shù)個(gè)數(shù),也可以把參數(shù)包展開,對(duì)每個(gè)參數(shù)做更多的事。展開的方法有兩種,遞歸函數(shù),逗號(hào)表達(dá)式。

遞歸函數(shù)方式展開,模板推導(dǎo)的時(shí)候,一層層遞歸展開,最后到?jīng)]有參數(shù)時(shí)用定義的一般函數(shù)終止。

void test()
{
}

template<class T, class... Args> 
void test(T first, Args... args)
{
 std::cout << typeid(T).name() << " " << first << std::endl;
 test(args...);
}

test<int, int, long>(0, 0, 0L);

output:
int 0
int 0
long 0

逗號(hào)表達(dá)式方式展開,利用數(shù)組的參數(shù)初始化列表和逗號(hào)表達(dá)式,逐一執(zhí)行print每個(gè)參數(shù)。

template<class T>
void print(T arg)
{
 std::cout << typeid(T).name() << " " << arg << std::endl;
}

template<class... Args>
void test(Args... args)
{
 int arr[] = { (print(args), 0)... };
}

test(0, 0, 0L);

output:
int 0
int 0
long 0

2.2變長(zhǎng)參數(shù)類模板

變長(zhǎng)參數(shù)類模板,一般情況下可以方便我們做一些編譯期計(jì)算??梢酝ㄟ^(guò)偏特化和遞歸推導(dǎo)的方式依次展開模板參數(shù)。

template<class T, class... Types>
class Test
{
public:
 enum {
  value = Test<T>::value + Test<Types...>::value,
 };
};

template<class T>
class Test<T>
{
public:
 enum {
  value = sizeof(T),
 };
};

Test<int, int, long> test;
std::cout << test.value;

output: 12

2.3右值引用和完美轉(zhuǎn)發(fā)

對(duì)于變長(zhǎng)參數(shù)函數(shù)模板,需要將形參包展開逐個(gè)處理的需求不多,更多的還是像本文的MemNew這樣的需求,最終整個(gè)傳入某個(gè)現(xiàn)有的函數(shù)。我們把重點(diǎn)放在參數(shù)的傳遞上。

要理解右值引用,需要先說(shuō)清楚左值和右值。左值是內(nèi)存中有確定存儲(chǔ)地址的對(duì)象的表達(dá)式的值;右值則是非左值的表達(dá)式的值。const左值不可被賦值,臨時(shí)對(duì)象的右值可以被賦值。左值與右值的根本區(qū)別在于是否能用&運(yùn)算符獲得內(nèi)存地址。

int i =0;//i 左值
int *p = &i;// i 左值
int& foo();
foo() = 42;// foo() 左值
int* p1 = &foo();// foo() 左值

int foo1();
int j = 0;
j = foo1();// foo 右值
int k = j + 1;// j + 1 右值
int *p2 = &foo1(); // 錯(cuò)誤,無(wú)法取右值的地址
j = 1;// 1 右值

理解左值和右值之后,再來(lái)看引用,對(duì)左值的引用就是左值引用,對(duì)右值(純右值和臨終值)的引用就是右值引用。

如下函數(shù)foo,傳入int類型,返回int類型,這里傳入函數(shù)的參數(shù)0和返回值0都是右值(不能用&取得地址)。于是,未做優(yōu)化的情況下,傳入?yún)?shù)0的時(shí)候,我們需要把右值0拷貝給param,函數(shù)返回的時(shí)候需要將0拷貝給臨時(shí)對(duì)象,臨時(shí)對(duì)象再拷貝給res。當(dāng)然現(xiàn)在的編譯器都做了返回值優(yōu)化,返回對(duì)象是直接創(chuàng)建在返回后的左值上的,這里只用來(lái)舉個(gè)例子

int foo(int param)
{
 printf("%d", param);
 return 0;
}

int res = foo(0);

顯然,這里的拷貝都是多余的。可能我們會(huì)想要優(yōu)化,首先將參數(shù)int改為int& , 傳入左值引用,于是0無(wú)法傳入了,當(dāng)然我們可以改成const int& ,這樣終于省去了傳參的拷貝。

int foo(const int& param)
{
 printf("%d", param);
 return 0;
}

由于const int& 既可以是左值也可以是右值,傳入0或者int變量都能夠滿足。(但是似乎既然有左值引用的int&類型,就應(yīng)該有對(duì)應(yīng)的傳入右值引用的類型int&& )。另外,這里返回的右值0,似乎不通過(guò)拷貝就無(wú)法賦值給左值res 。

于是有了移動(dòng)語(yǔ)義,把臨時(shí)對(duì)象的內(nèi)容直接移動(dòng)給被賦值的左值對(duì)象(std::move)。和右值引用,X&&是到數(shù)據(jù)類型X的右值引用。

int result = 0;
int&& foo(int&& param)
{
 printf("%d", param);
 return std::move(result);
}

int&& res = foo(0);
int *pres = &res;

foo改為右值引用參數(shù)和返回值,返回右值引用,免去拷貝。這里res是具名引用,運(yùn)算符右側(cè)的右值引用作為左值,可以取地址。右值引用既有左值性質(zhì),也有右值性質(zhì)。

上述例子還只存在于拷貝的性能問(wèn)題?;氐?code>MemNew這樣的函數(shù)模板。

template<class T>
T* Test(T arg)
{
 return new T(arg);
}

template<class T>
T* Test(T& arg)
{
 return new T(arg);
}

template<class T>
T* Test(const T& arg)
{
 return new T(arg);
}

template<class T>
T* Test(T&& arg)
{
 return new T(std::forward<T>(arg));
}

上述的前三種方式傳參,第一種首先有拷貝消耗,其次有的參數(shù)就是需要修改的左值。第二種方式則無(wú)法傳常數(shù)等右值。第三種方式雖然左值右值都能傳,卻無(wú)法對(duì)傳入的參數(shù)進(jìn)行修改。第四種方式使用右值引用,可以解決參數(shù)完美轉(zhuǎn)發(fā)的問(wèn)題。

  std::forward能夠根據(jù)實(shí)參的數(shù)據(jù)類型,返回相應(yīng)類型的左值和右值引用,將參數(shù)完整不動(dòng)的傳遞下去。

  解釋這個(gè)原理涉及到引用塌縮規(guī)則

      T& & ->T&

      T& &&->T&

           T&& &->T&

      T&& &&->T&&

template< class T > struct remove_reference  {typedef T type;};
template< class T > struct remove_reference<T&> {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};

template< class T > T&& forward( typename std::remove_reference<T>::type& t )
{
 return static_cast<T&&>(t);
}

template<class T>
typename std::remove_reference<T>::type&& move(T&& a) noexcept
{ 
 return static_cast<typename std::remove_reference<T>::type&&>(a);
}

對(duì)于函數(shù)模板

template<class T>
T* Test(T&& arg)
{
 return new T(std::forward<T>(arg));
}

當(dāng)傳入實(shí)參為X類型左值時(shí),T為X&,最后的類型為X&。當(dāng)實(shí)參為X類型右值時(shí),T為X,最后的類型為X&&。

x為左值時(shí):

X x;
Test(x);

T為X&,實(shí)例化后

X& && std::forward(remove_reference<X&>::type& a) noexcept
{
 return static_cast<X& &&>(a);
}

X* Test(X& && arg)
{
 return new X(std::forward<X&>(arg)); 
}

// 塌陷后

X& std::forward(X& a)
{
 return static_cast<X&>(a);
}

X* Test(X& arg)
{
 return new X(std::forward<X&>(arg));
}

x為右值時(shí):

X foo();
Test(foo());

T為X,實(shí)例化后

X&& std::forward(remove_reference<X>::type& a) noexcept
{
 return static_cast<X&&>(a);
}

X* Test(X&& arg)
{
 return new X(std::forward<X>(arg)); 
}

// 塌陷后

X&& std::forward(X& a)
{
 return static_cast<X&&>(a);
}

X* Test(X&& arg)
{
 return new X(std::forward<X>(arg));
}

可以看到最終實(shí)參總是被推導(dǎo)為和傳入時(shí)相同的類型引用。

至此,我們討論了變長(zhǎng)參數(shù)模板,討論了右值引用和函數(shù)模板的完美轉(zhuǎn)發(fā),完整的解釋了MemNew對(duì)任意多個(gè)參數(shù)的構(gòu)造函數(shù)的參數(shù)傳遞過(guò)程。利用變長(zhǎng)參數(shù)函數(shù)模板,右值引用和std::forward ,可以完成參數(shù)的完美轉(zhuǎn)發(fā)。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家學(xué)習(xí)或者使用C++能有所幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • C++ 異常處理 catch(...)介紹

    C++ 異常處理 catch(...)介紹

    catch(…)能夠捕獲多種數(shù)據(jù)類型的異常對(duì)象,所以它提供給程序員一種對(duì)異常 對(duì)象更好的控制手段,使開發(fā)的軟件系統(tǒng)有很好的可靠性
    2013-09-09
  • C++ Opencv imfill孔洞填充函數(shù)的實(shí)現(xiàn)思路與代碼

    C++ Opencv imfill孔洞填充函數(shù)的實(shí)現(xiàn)思路與代碼

    在Matlab下,使用imfill可以很容易的完成孔洞填充操作,下面這篇文章主要給大家介紹了關(guān)于C++ Opencv imfill孔洞填充函數(shù)的實(shí)現(xiàn)思路與代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-09-09
  • C語(yǔ)言入門篇--局部全局變量的作用域及生命周期

    C語(yǔ)言入門篇--局部全局變量的作用域及生命周期

    本篇文章是c語(yǔ)言基礎(chǔ)篇,本文對(duì)初識(shí)c語(yǔ)言的變量、局部全局變量的作用域及生命周期做了簡(jiǎn)要的概述,希望可以幫助大家快速入門c語(yǔ)言的世界,更好的理解c語(yǔ)言
    2021-08-08
  • 哈夫曼的c語(yǔ)言實(shí)現(xiàn)代碼

    哈夫曼的c語(yǔ)言實(shí)現(xiàn)代碼

    著先通過(guò) HuffmanTree() 函數(shù)構(gòu)造哈夫曼樹,然后在主函數(shù) main()中自底向上開始(也就是從數(shù)組序號(hào)為零的結(jié)點(diǎn)開始)向上層層判斷,若在父結(jié)點(diǎn)左側(cè),則置碼為 0,若在右側(cè),則置碼為 1。最后輸出生成的編碼
    2013-07-07
  • C++實(shí)現(xiàn)批量圖片拼接

    C++實(shí)現(xiàn)批量圖片拼接

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)批量圖片拼接,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • C++ Boost Array與Unordered使用介紹

    C++ Boost Array與Unordered使用介紹

    Boost是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱。Boost庫(kù)是一個(gè)可移植、提供源代碼的C++庫(kù),作為標(biāo)準(zhǔn)庫(kù)的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱
    2022-11-11
  • 深入理解C++中std::chrono庫(kù)的使用

    深入理解C++中std::chrono庫(kù)的使用

    在程序設(shè)計(jì)中,時(shí)間管理是一個(gè)核心概念,它不僅關(guān)系到程序的效率和性能,而且直接影響用戶體驗(yàn),C++作為一門高效的編程語(yǔ)言,提供了std::chrono庫(kù),用于精確地處理和計(jì)算時(shí)間,下面就跟隨小編一起學(xué)習(xí)一下std::chrono庫(kù)的使用吧
    2023-12-12
  • C++各種輸出數(shù)據(jù)類型詳解

    C++各種輸出數(shù)據(jù)類型詳解

    這篇文章主要介紹了C++各種輸出數(shù)據(jù)類型,在C++中,可以使用cout對(duì)象和插入運(yùn)算符<<輸出各種數(shù)據(jù)類型,包括整數(shù)類型、浮點(diǎn)數(shù)類型、字符類型、字符串類型和布爾類型,需要的朋友可以參考下
    2023-06-06
  • VC++中內(nèi)存對(duì)齊實(shí)例教程

    VC++中內(nèi)存對(duì)齊實(shí)例教程

    這篇文章主要介紹了VC++中內(nèi)存對(duì)齊的實(shí)現(xiàn)方法,具有很高的實(shí)用價(jià)值,需要的朋友可以參考下
    2014-08-08
  • C++友元函數(shù)與拷貝構(gòu)造函數(shù)詳解

    C++友元函數(shù)與拷貝構(gòu)造函數(shù)詳解

    這篇文章主要介紹了C++友元函數(shù)與拷貝構(gòu)造函數(shù),需要的朋友可以參考下
    2014-07-07

最新評(píng)論