一个关于包含头文件的简单问题,求怎么解决

purexiafeng 2009-06-11 02:11:49
背景大致是这样的
有2个类文件 A类和B类
在A类里的有个成员变量是B类
我想声明了个A类型的全局变量。想在B类的ADD方法里调用
但是我的A类型的全局变量定义无论放在哪里都会出错,我用了extern声明也不行。我小白,有人帮我解答下吗
A.H:

class A
{
public:
A(void);
public:
~A(void);
B bc;
void rem();
};

A.CPP:

A::A(void)
{
}

A::~A(void)
{
}

void A::rem()
{

}

B.H:

class B
{
public:
B(void);
public:
~B(void);
void add();
};


B.CPP:

B::B(void)
{
}

B::~B(void)
{
}


void B::add()
{
ac->rem();
}

...全文
19 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
wocow3 2009-06-11
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 purexiafeng 的回复:]
引用 1 楼 Ryanwen 的回复:

A.CPP
A  g_a;

A.H
extern A g_a;

B.CPP
#include"A.h"

这种方法可以解决,但有没有更好的处理方法。我希望能吧所有类的全局变量定义统一放在一起定义,而不是放在各自的CPP中
[/Quote]
那就另外建一个XX.CPP,定义所有变量;
声明全部放在XX.h中,extern A g_a;等等
用的时候,每个cpp最后包含XX.h
bragi523 2009-06-11
  • 打赏
  • 举报
回复
在B.h中加class A;
在B.h中加#include "A.h"
Jarrylogin 2009-06-11
  • 打赏
  • 举报
回复
你的类设计可能有问题!

设计如下:

// A.H

class A
{
public:
A(void);
public:
~A(void);
// B bc;
void rem();
};

extern A g_a;


// B.CPP:
#include "A.H"

A g_a;

B::B(void)
{
}

B::~B(void)
{
}


void B::add()
{
g_a.rem();
}

purexiafeng 2009-06-11
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 Ryanwen 的回复:]
A.CPP
A  g_a;

A.H
extern A g_a;

B.CPP
#include"A.h"
[/Quote]
这种方法可以解决,但有没有更好的处理方法。我希望能吧所有类的全局变量定义统一放在一起定义,而不是放在各自的CPP中
vcTiro 2009-06-11
  • 打赏
  • 举报
回复
// A.h:
class B;

// A.cpp:
#include "B.h"
purexiafeng 2009-06-11
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 season030441 的回复:]
在B类中的变量ac根本就没有定义过,当然不好使。
[/Quote]
我现在就是在哪定义都会编译出错!
season030441 2009-06-11
  • 打赏
  • 举报
回复
在B类中的变量ac根本就没有定义过,当然不好使。
Ryanwen 2009-06-11
  • 打赏
  • 举报
回复
A.CPP
A g_a;

A.H
extern A g_a;

B.CPP
#include"A.h"
为PDF文件增加了目录,便于快速浏览。 摘录一些不知道以及没有做到的: 1、防止多重包含头文件的形式不能完美做到) 所有头文件都应该使用#define防止头文件被多重包含(multiple inclusion),命名格式当是:___H_ 为保证唯一性,头文件的命名应基于其所在项目源代码树的全路径。例如,项目foo中的头文件foo/src/bar/baz.h按如下方式保护: #ifndef FOO_BAR_BAZ_H_ #define FOO_BAR_BAZ_H_ ... #endif // FOO_BAR_BAZ_H_ 2、能依赖声明就不要依赖定义 使用前置声明(forward declarations)尽量减少.h文件中#include的数量。 当一个头文件包含的同时也引入了一项新的依赖(dependency),只要该头文件被修改,代码就要重新编译。如果你的头文件包含了其他头文件,这些头文件的任何改变也将导致那些包含了你的头文件的代码重新编译。因此,我们宁可尽量少包含头文件,尤其是那些包含在其他头文件中的。 使用前置声明可以显著减少需要包含头文件数量。举例说明:头文件中用到类File,但不需要访问File的声明,则头文件中只需前置声明class File;无需#include "file/base/file.h"。 在头文件如何做到使用类Foo而无需访问类的定义? 1) 将数据成员类型声明为Foo *或Foo &; 2) 参数、返回值类型为Foo的函数只是声明(但不定义实现); 3) 静态数据成员的类型可以被声明为Foo,因为静态数据成员的定义在类定义之外。 另一方面,如果你的类是Foo的子类,或者含有类型为Foo的非静态数据成员,则必须为之包含头文件。 3、为什么要使用内联函数,又如何改进性能? 定义(Definition):当函数被声明为内联函数之后,编译器可能会将其内联展开,无需按通常的函数调用机制调用内联函数。 重要的是,虚函数和递归函数即使被声明为内联的也不一定就是内联函数。通常,递归函数不应该被声明为内联的(译者注:递归调用堆栈的展开并不像循环那么简单,比如递归层数在编译时可能是未知的,大多数编译器都不支持内联递归函数)。析构函数内联的主要原因是其定义在类的定义中,为了方便抑或是对其行为给出文档。 4. 函数参数顺序(Function Parameter Ordering) 定义函数时,参数顺序为:输入参数在前,输出参数在后。 C/C++ 函数参数分为输入参数和输出参数两种,有时输入参数也会输出(译者注:值被修改时)。输入参数一般传值或常数引用(const references),输出参数或输入/输出参数为非常数指针(non-const pointers)。对参数排序时,将所有输入参数置于输出参数之前。不要仅仅因为是新添加的参数,就将其置于最后,而应该依然置于输出参数之前。 5、头文件定义顺序 将包含次序标准化可增强可读性、避免隐藏依赖(hidden dependencies,译者注:隐藏依赖主要是指包含的文件中编译时),次序如下:对应该CPP的头文件、C库、C++库、其他库的.h、项目内的.h。 相同目录下头文件按字母序是不错的选择。 1. 命名空间(Namespaces) 在.cc文件中,提倡使用不具名的命名空间(unnamed namespaces,译者注:不具名的命名空间就像不具名的类一样,似乎被介绍的很少:-()。使用具名命名空间时,其名称可基于项目或路径名称,不要使用using指示符。 定义:命名空间将全局作用域细分为不同的、具名的作用域,可有效防止全局作用域的命名冲突。 优点:命名空间提供了(可嵌套)命名轴线(name axis,译者注:将命名分割在不同命名空间内),当然,类也提供了(可嵌套)的命名轴线(译者注:将命名分割在不同类的作用域内)。 举例来说,两个不同项目的全局作用域都有一个类Foo,这样在编译或运行时造成冲突。如果每个项目将代码置于不同命名空间中,project1::Foo和project2::Foo作为不同符号自然不会冲突。 缺点:命名空间具有迷惑性,因为它们和类一样提供了额外的(可嵌套的)命名轴线。在头文件中使用不具名的空间容易违背C++的唯一定义原则(One Definition Rule (ODR))。 1) 不具名命名空间(Unnamed Namespaces) 在.cc文件中,允许甚至提倡使用不具名命名空间,以避免运行时的命名冲突: namespace { // .cc 文件中 // 命名空间的内容无需缩进 enum { UNUSED, EOF, ERROR }; // 经常使用的符号 bool AtEof() { return pos_ == EOF; } // 使用本命名空间内的符号EOF } // namespace 然而,与特定类关联的文件作用域声明在该类中被声明为类型、静态数据成员或静态成员函数(什么意思?),而不是不具名命名空间的成员。像上文展示的那样,不具名命名空间结束时用注释// namespace标识。 2、函数重载 结论:如果你想重载一个函数,考虑让函数名包含参数信息,例如,使用AppendString()、AppendInt()而不是Append()。 3. 缺省参数(Default Arguments) 禁止使用缺省函数参数。 优点:经常用到一个函数带有大量缺省值,偶尔会重写一下这些值,缺省参数为很少涉及的例外情况提供了少定义一些函数的方便。 缺点:大家经常会通过查看现有代码确定如何使用API,缺省参数使得复制粘贴以前的代码难以呈现所有参数,当缺省参数不适用于新代码时可能导致重大问题。 结论:所有参数必须明确指定,强制程序员考虑API和传入的各参数值,避免使用可能不为程序员所知的缺省参数。 8. 类型转换(Casting) 使用static_cast<>()等C++的类型转换,不要使用int y = (int)x或int y = int(x);。 定义:C++引入了有别于C的不同类型的类型转换操作。 优点:C语言的类型转换问题在于操作比较含糊:有时是在做强制转换(如(int)3.5),有时是在做类型转换(如(int)"hello")。另外,C++的类型转换查找更容易、更醒目。 缺点:语法比较恶心(nasty)。 结论:使用C++风格而不要使用C风格类型转换。 1) static_cast:和C风格转换相似可做值的强制转换,或指针的父类到子类的明确的向上转换; 2) const_cast:移除const属性; 3) reinterpret_cast:指针类型和整型或其他指针间不安全的相互转换,仅在你对所做一切了然于心时使用; 4) dynamic_cast:除测试外不要使用,除单元测试外,如果你需要在运行时确定类型信息,说明设计有缺陷(参考RTTI)。 10. 前置自增和自减(Preincrement and Predecrement) 对于迭代器和其他模板对象使用前缀形式(++i)的自增、自减运算符。 定义:对于变量在自增(++i或i++)或自减(--i或i--)后表达式的值又没有没用到的情况下,需要确定到底是使用前置还是后置的自增自减。 优点:不考虑返回值的话,前置自增(++i)通常要比后置自增(i++)效率更高,因为后置的自增自减需要对表达式的值i进行一次拷贝,如果i是迭代器或其他非数值类型,拷贝的代价是比较大的。既然两种自增方式动作一样(译者注,不考虑表达式的值,相信你知道我在说什么),为什么不直接使用前置自增呢? 缺点:C语言中,当表达式的值没有使用时,传统的做法是使用后置自增,特别是在for循环中,有些人觉得后置自增更加易懂,因为这很像自然语言,主语(i)在谓语动词(++)前。 结论:对简单数值(非对象)来说,两种都无所谓,对迭代器和模板类型来说,要使用前置自增(自减)。 16. sizeof(sizeof) 尽可能用sizeof(varname)代替sizeof(type)。 使用sizeof(varname)是因为当变量类型改变时代码自动同步,有些情况下sizeof(type)或许有意义,还是要尽量避免,如果变量类型改变的话不能同步。 8. TODO注释(TODO Comments) 对那些临时的、短期的解决方案,或已经够好但并不完美的代码使用TODO注释。 这样的注释要使用全大写的字符串TODO,后面括号(parentheses)里加上你的大名、邮件地址等,还可以加上冒号(colon):目的是可以根据统一的TODO格式进行查找: // TODO(kl@gmail.com): Use a "*" here for concatenation operator. // TODO(Zeke) change this to use relations. 如果加上是为了在“将来某一天做某事”,可以加上一个特定的时间("Fix by November 2005")或事件("Remove this code when all clients can handle XML responses.")。 TODO很不错,有时候,注释确实是为了标记一些未完成的或完成的不尽如人意的地方,这样一搜索,就知道还有哪些活要干,日志都省了。

16,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧