.h⽂件和.cpp⽂件组织结构
1、包含关系:
.h⽂件⼀般包含在.cpp⽂件中,.h⽂件中多为变量和类的声明,⽽.cpp⽂件才是变量和类中函数的真正定义。
但是#include <iostream> 这个⽂件既不是.c也不是.h,那我们能不能不⽤它,改⽤iostream.h?⼀般来说只是为了使⽤cout这种对象是可以的。但意义上有差别,iostream和iostream.h是不⼀样的,⼀个是模板库,⼀个是C++库头⽂件。这⾥顺便提起⼀下以前的⼀件事,我刚进⼊上上家公司的时候,头要我们写个类模板,于是我就跟写⼀般的类⼀样把它分为两个⽂件,⼀个.h,⼀个.cpp,⼀个放定义,⼀个放实现。你们认为我的做法有问题么?写过模板的⼈都应该知道,这样是不⾏的,因为C++中的实现是针对类的实现,⽽不是针对模板的实现,分开写会导致连接时候不到实现的。因此必须将模板的“定义”与“实现”写在同⼀个⽂件中,那这个⽂件究竟叫“.h”好呢还是“.cpp”好呢?都不好,它既不是类的定义也不是类的实现,它是模板,模板就是模板,那⼲脆就不⽤⽂件扩展名,STL就怎么⼲的,That’s OK!
摘⾃:
2、基类中有派⽣类的指针,或者A类中有B类指针,B类中有A类指针
基类为CBase,⼦类有CDerivedA,CDerivedB,这倒没什么,但CBase中竟然有这种函数:
class CBase
{
//……
virtual CDerivedA* GetA();
virtual CDerivedB* GetB();
};
DerivedA.h和DerivedB.h中需要include Base.h,⽽CBase竟然也⽤到了它的⼦类……那根据哪⾥⽤到就哪⾥包含的法则,Base.h是不是也要include DerivedA.h和DerivedB.h?这岂不是形成了“互相包含”?是的,如果出现了这种互相包含,VC++就会给出编译警告。当然如果你做了“反重复包含” 的⼯作,编译警告就不会出现,也不会出现“重复定义”,⽽取⽽代之的是“未定义”,程序还是通不过的。⽐如下⾯这个简单的例⼦:
//基类 Base.h
#ifndef __BASE_H__#define __BASE_H__
#include"DerivedA.h"
class CBase
{
public:
CBase();
~CBase();
virtual CDerivedA*GetA(){return NULL;}
};
#endif
//⼦类 DerivedA.h
#ifndef __DERIVED_A_H__
#define __DERIVED_A_H__
#include"Base.h"
classCDerivedA:public CBase
{
public:
CDerivedA();
~CDerivedA();
virtual CDerivedA* GetA()
{return this;}
};
#endif
编译出现的错误⼤致如下,但并⾮⼀定,甚⾄每次都有可能不同,这和编译的顺序有关系:
error C2143:syntax error : missing ';' before '*'
error C2433:'CDerivedA' : 'virtual' not permitted on data declarations
error C2501:'CDerivedA' : missing storage-class or type specifiers
error C2501:'GetA' : missing storage-class or type specifiers
总之出现了这种基类“需要”⼦类的情况的话,就不能这样include了。取⽽代之的是使⽤⼀个类的声明:在Base.h中把“#include "DerivedA.h"”去掉,⽤“class CDerivedA;”取代它。这样编译就没有问题了。
OK OK,可能你⼜有问题了,如果基类中的函数不是“virtual CDerivedA* GetA()”,⽽是“virtual CDerivedA GetA()”,那怎么⼜通不过了?哇哈哈……⽼兄,你别扯了,我保证你遍全世界的⾼⼿,也没有⼈能解决这个问题的,因为它逻辑上已经错误了,⽗在诞⽣的时候需要⼦,⽽⽗没诞⽣,哪来的⼦?⼜⼀个典型的鸡⽣蛋,蛋⽣鸡的问题。⾄于指针为什么就可以,因为指针在Win32中归根到底只是⼀个long型,它并不需要理解CDerivedA究竟何⽅神圣,只需要连接的时候到就⾏了,反过来如果不是指针,CBase就要尝试寻CDerivedA并⽣成实例,这可能吗?
注意:在派⽣类从基类继承时,此时必须要有基类的完整声明,不能只有前向声明,即class Base;这样是不能通过编译的。如下代码:注意是错的
#include<iostream>
using namespace std;
class A;
class B:public A
{
};
class A
{
int m;
public:
void fun();
};
int main()
{
A aa;
B bb;
system("pause");
return 0;
}
输出的错误提⽰为:error C2504: 'A' : base class undefined
3、注意基类中有派⽣类时的构造函数
/
/a.h
#ifndef _AAA_
#define _AAA_
class Derived;
class Base
{
protected:
Derived *p;
public:
Base(){};
Base(Derived &d);
~
Base(){}
void fun();
};
#endif
//a.cpp
#include"a.h"
#include"b.h"
Base::Base(Derived &d)
{
p=&d;
p=&d;
}
void Base::fun()
{
//可以调⽤Derived类中的函数,因为b.h中有函数声明
p->getA();
}
//b.h
#ifndef _BBB_
#define _BBB_
#include"a.h"
class Derived:public Base
{
int a;
int b;
public:
Derived(){}
Derived(int _a,int _b);
~Derived(){}
int getA();
};
#endif
//b.cpp
#include"b.h"
#include<iostream>
Derived::Derived(int _a,int _b)
{
a=_a;
system的头文件b=_b;
p=new Derived();
std::cout<<"111"<<std::endl;
}
int Derived::getA()
{
std::cout<<a<<std::endl;
return a;
}
//test.cpp
#include<iostream>
#include"a.h"
#include"b.h"
using namespace std;
int main()
{
Base a;
Derived d(1,2);
Base b(d);
b.fun();
system("pause");
return 0;
}
如果基类只有⼀个构造函数,那么这个构造函数必须有缺省参数,⽽且只能复制为0,这样在构造派⽣类对象时,调⽤基类的构造函数,就不会存在再调⽤派⽣类构造函数的⽆限循环中了,即派⽣类中绝不能出现p=new Derived();因为这样将回到先有鸡还是先有蛋的问题上了。
3、如果不⽤头⽂件,⽽直接定义基类和派⽣类⼜会有问题:如下是⼈和狗的问题,即Man类中有Dogs类的指针,表⽰⼈拥有的狗,⽽Dogs类中也有Man的指针,表⽰狗属于的⼈。这样如果先写Dogs类,就需要前向声明Man类,但是在Dogs类中的void printmsg();函数需要调⽤Man类中的char *getName()来得到名字,但是此时只有Man进⾏了前向声明,⽆法得知char *getName()函数的任何信息,因此就需要将void printmsg();函数放到char *getName()函数的声明或者定义之后。
#include <iostream>
using namespace std;
class Man;
class Dogs
{
public:
char name[20];
Man *master;
Dogs(char *name,Man *m):master(m)
{
strcpy(this->name,name);
}
Dogs(char *name)
{
strcpy(this->name,name);
master=NULL;
}
void addMasterMsg(Man &m)
{
master=&m;
}
//在这⾥不能定义,因为⽤到了Man类中的函数,⽽Man类在前⾯只是声明了类,
/
/并没有声明函数,因此不能⽤Man类中的函数
void printmsg();
};
class Man
{
char name[20];
public:
Dogs *dogs[10];
Man(char *name)
{
strcpy(this->name,name);
for(int i=0;i<10;i++)
{
dogs[i]=NULL;
}
}
void addDog(Dogs *dog,int i)
{
dogs[i-1]=dog;
}
void printmes()
{
cout<<name<<"的狗有:"<<endl;
for(int i=0;i<10;i++)
{
if(dogs[i])
cout<<"第"<<i<<"只狗名字是:"<<dogs[i]->name<<endl;
}
}
char *getName()
{
return name;
}
}
};
void Dogs::printmsg()
{
cout<<name<<"是"<<master->getName()<<"的狗"<<endl;
}
int main()
{
Man m1("zhangsan");
cout<&Name()<<endl;
Dogs d1=("dog1");
d1.addMasterMsg(m1);
m1.addDog(&d1,1);
// Dogs d2=("dog2",&m1);不知道为什么这样做构造函数不⾏,⽽且构造函数⽤初始化列表也不⾏。
//只能如上,⽤add函数增加
//此处应该⽤Dogs d2=Dogs(“dog2”,&m1);才对,这样⽤的是默认的拷贝构造函数,临时构造⼀个对象,
//完成拷贝,或者Dogs d2(“dog2”,&m1);也可以,千万不能⽤上⾯所⽰的错误的代码。赋值根本就不合//语法
Dogs d3=Dogs("dog3",&m1);
Dogs d4("dog4",&m1);
m1.printmes();
d1.printmsg();
cout<<sizeof(Man)<<" "<<sizeof(Dogs)<<endl;
system("pause");
return 0;
}
在这⾥如果有多个函数需要调⽤不同类之间的函数,就需要不断的进⾏声明,或者进⾏顺序化的定义,防⽌⼀个函数定义时,其调⽤的函数还没有声明。但是如
果利⽤.h和.c⽂件来组织程序,就能够避免混乱的程序代码。此时,只需要分别定义Dogs.h实现Dogs类声明,当然要前向声明class Man,Man.h实现Man类
声明,当然也要前向声明class Dogs,或者包含Dogs.h⽂件。然后在定义Dogs.cpp和Man.cpp都包含Dogs.h和Man.h即可。完美的解决了上⾯的问题,⽽
且思路很清晰,结构也很明了。
4、注意extern和static关键字
extern表⽰声明变量,⼀般⽤在.h⽂件中,⽽定义放在.cpp⽂件中,static关键字其实是为了屏蔽extern的声明,或者说是当前.cpp⽂件中的全局变量,但是不会被其他
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论