C++中hpp的适用
本文第一部分转载百度百科,在此感谢百度。第二部分示例为独立编写。
hpp,其实质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该hpp文件即可,无需再将cpp加入到project中进行编译。而实现代码将直接编译到调用者的obj文件中,不再生成单独的obj,采用hpp将大幅度减少调用 project中的cpp文件数与编译次数,也不用再发布烦人的lib与dll,因此非常适合用来编写公用的开源库。
hpp的优点不少,但是编写中有以下几点要注意:
1、是Header Plus Plus 的简写。
2、与*.h类似,hpp是C++程序头文件 。
3、是VCL专用的头文件,已预编译。
4、是一般模板类的头文件。
5、一般来说,*.h里面只有声明,没有实现,而*.hpp里声明实现都有,后者可以减少.cpp的数量。
6、*.h里面可以有using namespace std,而*.hpp里则无。
7、不可包含全局对象和全局函数。
由于hpp本质上是作为.h被调用者include,所以当hpp文件中存在全局对象或者全局函数,而该hpp被多个调用者include时,将在链接时导致符号重定义错误。要避免这种情况,需要去除全局对象,将全局函数封装为类的静态方法。
8、类之间不可循环调用。
在.h和.cpp的场景中,当两个类或者多个类之间有循环调用关系时,只要预先在头文件做被调用类的声明即可,如下:
class B;
class A{
public:
void someMethod(B b);
};
class B{
public :
void someMethod(A a);
};
在hpp场景中,由于定义与实现都已经存在于一个文件,调用者必需明确知道被调用者的所有定义,而不能等到cpp中去编译。因此hpp中必须整理类之间调用关系,不可产生循环调用。同理,对于当两个类A和B分别定义在各自的hpp文件中,形如以下的循环调用也将导致编译错误:
//a.hpp
#include "b.hpp"
class A{
public :
void someMethod(B b);
};
//b.hpp
#include "a.hpp"
class B{
public :
void someMethod(A a);
};
9、不可使用静态成员。
静态成员的使用限制在于如果类含有静态成员,则在hpp中必需加入静态成员初始化代码,当该hpp被多个文档include时,将产生符号重定义错误。唯一的例外是const static整型成员,因为在vs2003中,该类型允许在定义时初始化,如:
class A{
public:
const static int intValue = 123;
};
由于静态成员的使用是很常见的场景,无法强制清除,因此可以考虑以下几种方式(以下示例均为同一类中方法)
1.类中仅有一个静态成员时,且仅有一个调用者时,可以通过局域静态变量模拟
//方法模拟获取静态成员
someType getMember()
{
static someType value(xxx);//作用域内静态变量
return value;
}
2.类中有多个方法需要调用静态成员,而且可能存在多个静态成员时,可以将每个静态成员封装一个模拟方法,供其他方法调用。
someType getMemberA()
{
static someType value(xxx);//作用域内静态变量
return value;
}
someType getMemberB()
{
static someType value(xxx);//作用域内静态变量
return value;
}
void accessMemberA()
{
someType member = getMemberA();//获取静态成员
};
//获取两个静态成员
void accessStaticMember()
{
someType a = getMemberA();//获取静态成员
someType b = getMemberB();
};
3.第二种方法对于大部分情况是通用的,但是当所需的静态成员过多时,编写封装方法的工作量将非常巨大,在此种情况下,建议使用Singleton模式,将被调用类定义成普通类,然后使用Singleton将其变为全局唯一的对象进行调用。
如原h+cpp下的定义如下:
class A{
public :
type getMember(){
return member;
}
static type member;//静态成员
}
采用singleton方式,实现代码可能如下(singleton实现请自行查阅相关文档)
//实际实现类
class Aprovider{
public :
type getMember(){
return member;
}
type member;//变为普通成员
}
//提供给调用者的接口类
class A{
public :
type getMember(){
return Singleton<AProvider >::getInstance()->getMember();
}
}
目前,模板的声明与定义需要在同一个文件中。如果分开,g++编译链接会报错。即使有一定的方法去模板的声明与定义分开,但是鉴于编译器的支持,不建议这么做。而hpp正好提供了这样的适用性,所以,无论是类模板还是函数模板,都在*.hpp文件中。在此,给个Demo,方便大家学习。
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <string> 5 #include <typeinfo> 6 #include "adapter.hpp" 7 using namespace std; 8 9 struct Base {}; // non-polymorphic 10 struct Derived : Base {}; 11 12 struct Base2 { virtual void foo() {} }; // polymorphic 13 struct Derived2 : Base2 {}; 14 15 int main() { 16 Test ttdata; 17 ttdata.data = 2; 18 Test ttdata2; 19 ttdata2.data = 5; 20 TestCom::compare<struct Test>(ttdata, ttdata2); 21 22 A a; 23 Test2 tt; 24 a.foo<Test2>(tt); 25 Test tst1; 26 Test2 tst2; 27 a.foo<Test,Test2>(tst1, tst2); 28 int myint = 50; 29 //int intx = 100; 30 if(typeid(myint).name() == typeid(int).name()) 31 cout << "same:" << typeid(myint).name() << endl; 32 std::string mystr = "string"; 33 double *mydoubleptr = NULL; 34 35 std::cout << "myint has type: " << typeid(myint).name() << '\n' 36 << "mystr has type: " << typeid(mystr).name() << '\n' 37 << "mydoubleptr has type: " << typeid(mydoubleptr).name() << '\n'; 38 39 // std::cout << myint is a glvalue expression of polymorphic type; it is evaluated 40 const std::type_info& r1 = typeid(std::cout << myint); 41 std::cout << '\n' << "std::cout<<myint has type : " << r1.name() << '\n'; 42 43 // std::printf() is not a glvalue expression of polymorphic type; NOT evaluated 44 const std::type_info& r2 = typeid(printf("%d\n", myint)); 45 std::cout << "printf(\"%d\\n\",myint) has type : " << r2.name() << '\n'; 46 47 // Non-polymorphic lvalue is a static type 48 Derived d1; 49 Base& b1 = d1; 50 std::cout << "reference to non-polymorphic base: " << typeid(b1).name() << '\n'; 51 52 Derived2 d2; 53 Base2& b2 = d2; 54 std::cout << "reference to polymorphic base: " << typeid(b2).name() << '\n'; 55 56 try { 57 // dereferencing a null pointer: okay for a non-polymorphic expression 58 std::cout << "mydoubleptr points to " << typeid(*mydoubleptr).name() << '\n'; 59 // dereferencing a null pointer: not okay for a polymorphic lvalue 60 Derived2* bad_ptr = NULL; 61 std::cout << "bad_ptr points to... "; 62 std::cout << typeid(*bad_ptr).name() << '\n'; 63 } catch (const std::bad_typeid& e) { 64 std::cout << " caught " << e.what() << '\n'; 65 } 66 }
1 #include "solution.h" 2 3 class TestCom{ 4 public: 5 template<typename T> 6 static void compare(const T& v1, const T& v2); 7 }; 8 9 template<typename T> 10 void TestCom::compare(const T& v1, const T& v2) 11 { 12 if(typeid(T) == typeid(struct Test)) 13 { 14 if(v1.data < v2.data) 15 cout << v1.data << " " << v2.data<< " v1 < v2" << endl; 16 if(v1.data > v2.data) 17 cout << v1.data << " " << v2.data<< " v1 > v2" << endl; 18 }else{ 19 printf("T is not Test : %s\n", typeid(Test2).name()); 20 } 21 } 22 23 //struct A{ 24 // void func(); 25 // template < typename T1 > void foo(T1 param); 26 //}; 27 28 struct A 29 { 30 void func(){cout << "func()" << endl;} 31 template < typename T1 > void foo(T1 param) { 32 //if(typeid(Test2).name() == typeid(param).name()){ 33 if(typeid(Test2) == typeid(T1)){ 34 param.data = 100; 35 string str("Test2 copy"); 36 strncpy(param.buf, str.c_str(), str.size()); 37 cout << "int:" << param.data << " " << param.buf << " type:" << typeid(param).name()<< endl; 38 }else{ 39 printf("err, though equal,but not reg, %s\n", typeid(T1).name()); 40 } 41 } 42 43 template < typename T1, typename T2 > void foo(T1 param, T2 param2) { 44 //if(typeid(Test2).name() == typeid(param).name()){ 45 if(typeid(T2) == typeid(T1)){ 46 param.data = 50; 47 param2.data = 100; 48 printf("param:%d, param2:%d\n", param.data, param2.data); 49 }else if ( typeid(Test2) == typeid(param2)) { 50 param2.data = 100; 51 string str("Test2 copy"); 52 strncpy(param2.buf, str.c_str(), str.size()); 53 cout << "int:" << param2.data << " " << param2.buf << " type:" << typeid(param2).name()<< endl; 54 } else{ 55 printf("err, though equal,but not reg, %s\n", typeid(T1).name()); 56 } 57 } 58 };
1 #ifndef _PROXY_H_ 2 #define _PROXY_H_ 3 #include <string> 4 #include <string.h> 5 using namespace std; 6 7 struct Test{ 8 int data; 9 //string str; 10 float f; 11 }; 12 13 struct Test2{ 14 int data; 15 //string str; 16 char buf[64]; 17 Test2(){ 18 data = 0; 19 memset(buf, 0x00, sizeof(buf)); 20 } 21 }; 22 23 #endif