分类目录归档:C++

函数调用在哪里?

今天看到一段函数,如下:

std::string comm_done_filename(const std::string & base_done_filename, const int & job_id) {
    return base_done_filename + ".done." + IntToString(job_id);
}

std::string comm_avg_model_name(const std::string & base_model_filename, const int &count) {
    return base_model_filename + ".avg." + IntToString(count);
}

隐约觉得可以用宏来解决, 因为函数名中的“done” 和“avg”,与返回值中包含的是相同的,而且两个函数形式完全一样,没有必要写成两个函数。

代码如下:

#define BT(name,flags,base,count) \
    std::string comm_##flags##_##name (const std::string & base,const int & count) { \
    return base + "."+ #flags + "." + IntToString(count); \
}

BT(model_name,avg,kaka,akak)
BT(filename,done,kaka,akak)

注意:函数名中的flags前后要有两个##, 而name只有前面有##,因为name之后没有东西了。

声名中kaka, akak是没有什么意义的,起个形参的作用。

调用的时候,当然可以直接用函数名:comm_done_filename,comm_avg_model_name。

另外,也可以使用函数指针数组或者用map。

首先,定义一个函数指针:

typedef std::string (*TransFun)(const std::string &, const int &);

函数指针数组如下:

 TransFun fun[] = {comm_done_filename,comm_avg_model_name};

map如下:

 map<string,TransFun> FunStrMap;

调用的时候当然是看不到函数名的:

 string aa = FunStrMap["done"]("cheng",3);
 string AA = fun[0]("cheng",3);

不多说,整个代码如下:

#include <iostream>
#include <string>
#include <sstream>
#include <map>

using namespace std;

std::string IntToString(const int &i) {
    std::stringstream ss;
    ss << i;
    return ss.str();
}
#define BT(name,flags,base,count) \
    std::string comm_##flags##_##name (const std::string & base,const int & count) { \
    return base + "."+ #flags + "." + IntToString(count); \
}

BT(model_name,avg,kaka,akak)
BT(filename,done,kaka,akak)

/*
std::string comm_done_filename(const std::string & base_done_filename, const int & job_id) {
    return base_done_filename + ".done." + IntToString(job_id);
}

std::string comm_avg_model_name(const std::string & base_model_filename, const int &count) {
    return base_model_filename + ".avg." + IntToString(count);
}
*/
typedef std::string (*TransFun)(const std::string &, const int &);
int main(){

    TransFun fun[] = {comm_done_filename,comm_avg_model_name};
    map<string,TransFun> FunStrMap;
    FunStrMap["done"] = fun[0];
    //FunStrMap["avg"] = fun[1];
    FunStrMap.insert(pair<string,TransFun>("avg",fun[1]));//不同添加元素的方法
    string aa = FunStrMap["done"]("cheng",3);
    string AA = fun[0]("cheng",3);
    string bb = FunStrMap["avg"]("yu",8);
    string BB = fun[1]("yu",8);
    cout<<aa<<"\n"<<bb<<endl;
    cout<<AA<<"\n"<<BB<<endl;
    return 0;

}

输出结果:

cheng.done.3
yu.avg.8
cheng.done.3
yu.avg.8

总结一下知识点:

宏定义,函数指针,函数指针数组、map

C++之static_cast

cplusplus.com中的解释:

“static_cast can perform conversions between pointers to related classes, not only upcasts (from pointer-to-derived to pointer-to-base), but also downcasts (from pointer-to-base to pointer-to-derived). No checks are performed during runtime to guarantee that the object being converted is in fact a full object of the destination type. Therefore, it is up to the programmer to ensure that the conversion is safe. On the other side, it does not incur the overhead of the type-safety checks of dynamic_cast.

1
2
3
4
classBase {};classDerived:publicBase {};
Base * a =newBase;
Derived * b =static_cast<Derived*>(a);

This would be valid code, although b would point to an incomplete object of the class and could lead to runtime errors if dereferenced.
Therefore, static_cast is able to perform with pointers to classes not only the conversions allowed implicitly, but also their opposite conversions.
static_cast is also able to perform all conversions allowed implicitly (not only those with pointers to classes), and is also able to perform the opposite of these. It can:

  • Convert from void* to any pointer type. In this case, it guarantees that if the void* value was obtained by converting from that same pointer type, the resulting pointer value is the same.
  • Convert integers, floating-point values and enum types to enum types.

Additionally, static_cast can also perform the following:

  • Explicitly call a single-argument constructor or a conversion operator.
  • Convert to rvalue references.
  • Convert enum class values into integers or floating-point values.
  • Convert any type to void, evaluating and discarding the value.

从上面的内容来看,static_cast的功能还是很多的。记录重要的几点:

1. 相当于C语言的强制类型转换。如把int转成size_t:static_cast<size_t> i_value

2. 做基类的下行转换时,不安全,建议用dynamic_cast.

3. 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。

4. 把空指针转换成目标类型的空指针。

5. 把任何类型的表达式转换成void类型。

6. static_cast不能转换掉expression的const、volatile、或者__unaligned属性

C++之reinterpret_cast

在cplusplus.com中的解释如下:

reinterpret_cast converts any pointer type to any other pointer type, even of unrelated classes. The operation result is a simple binary copy of the value from one pointer to the other. All pointer conversions are allowed: neither the content pointed nor the pointer type itself is checked.

It can also cast pointers to or from integer types. The format in which this integer value represents a pointer is platform-specific. The only guarantee is that a pointer cast to an integer type large enough to fully contain it (such as intptr_t), is guaranteed to be able to be cast back to a valid pointer.

The conversions that can be performed by reinterpret_cast but not by static_cast are low-level operations based on reinterpreting the binary representations of the types, which on most cases results in code which is system-specific, and thus non-portable.

大意是说主要用于指针类型的转换,以及指针与足够大的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型,无视大小。比特位不变。安全性由程序员自己保证

判断大小端及字节交换

之前写过一篇“如何判断机器的CPU是大端还是小端?http://www.orangespeech.com/?p=89”的blog, 现在更新一下开源代码中的简洁实现:

int IsLittleEndian() {
int check = 1;
  return (*reinterpret_cast<char*>(&check) != 0);
}

C的实现如下:

 #define BIG_ENDIAN      0
 #define LITTLE_ENDIAN   1

  int little_endian(void)
  {
      short int w = 0x0001;
      char *byte = (char *) &w;
      return(byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
  }

顺带给出字节交换的代码,同样厉害!

#define SWAP8(a) { \
  int t = ((char*)&a)[0]; ((char*)&a)[0]=((char*)&a)[7]; ((char*)&a)[7]=t;\
      t = ((char*)&a)[1]; ((char*)&a)[1]=((char*)&a)[6]; ((char*)&a)[6]=t;\
      t = ((char*)&a)[2]; ((char*)&a)[2]=((char*)&a)[5]; ((char*)&a)[5]=t;\
      t = ((char*)&a)[3]; ((char*)&a)[3]=((char*)&a)[4]; ((char*)&a)[4]=t;}
#define SWAP4(a) { \
  int t = ((char*)&a)[0]; ((char*)&a)[0]=((char*)&a)[3]; ((char*)&a)[3]=t;\
      t = ((char*)&a)[1]; ((char*)&a)[1]=((char*)&a)[2]; ((char*)&a)[2]=t;}
#define SWAP2(a) { \
  int t = ((char*)&a)[0]; ((char*)&a)[0]=((char*)&a)[1]; ((char*)&a)[1]=t;}

纯虚类与dynamic_cast

今天看到一段代码,用dynamic_cast将每个vector的成员cast到基类。但是,如果基类是一个纯虚类,似乎没有这个必要。以下是我的实验:

#include <iostream>
#include <vector>

using namespace std;

class Father{
        public:
                virtual void get()=0;
};
class Son: public Father{
        public:
                void get(){ cout<<"Son"<<endl;}
};

class Daughter: public Father{
        public:
                void get() {cout<<"Doughter"<<endl;}
};

int main(){

        vector<Father *> a;

        Son s1;
        a.push_back(&s1);
        Daughter d1;
        a.push_back(&d1);

        for(size_t i=0;i<a.size();i++){
            //Father *ts = dynamic_cast<Father *>(a[i]);
            //ts->get();
            a[i]->get();//可以获得与上两行一样的结果
        }
        return 0;
}