2010-04-27 42 views
1

我在教自己编写C++类,但似乎无法使编译通过。如果你能帮助我找出不仅如何,但为什么,这将不胜感激。先谢谢你!这里是我的三个文件:将C++类拆分为文件现在不会编译

make_pmt.C

#include <iostream> 
#include "pmt.h" 

using namespace std; 


int main() { 
    CPMT *pmt = new CPMT; 
    pmt->SetVoltage(900); 
    pmt->SetGain(2e6); 

    double voltage = pmt->GetVoltage(); 
    double gain= pmt->GetGain(); 

    cout << "The voltage is " << voltage 
     << " and the gain is " << gain << "." <<endl; 

    return 0; 
} 

pmt.C

#include "pmt.h" 

using namespace std; 

class CPMT { 
    double gain, voltage; 
    public: 
     double GetGain() {return gain;} 
     double GetVoltage() {return voltage;} 

     void SetGain(double g) {gain=g;} 
     void SetVoltage(double v) {voltage=v;} 
}; 

pmt.h

#ifndef PMT_H 
#define PMT_H 1 

using namespace std; 

class CPMT { 
    double gain, voltage; 
    public: 
     double GetGain(); 
     double GetVoltage(); 

     void SetGain(double g); 
     void SetVoltage(double v); 
}; 

#endif 

而对于重扰,我得到一个链接错误(右):

Undefined symbols: 
    "CPMT::GetVoltage()", referenced from: 
     _main in ccoYuMbH.o 
    "CPMT::GetGain()", referenced from: 
     _main in ccoYuMbH.o 
    "CPMT::SetVoltage(double)", referenced from: 
     _main in ccoYuMbH.o 
    "CPMT::SetGain(double)", referenced from: 
     _main in ccoYuMbH.o 
ld: symbol(s) not found 
collect2: ld returned 1 exit status 
+4

在标题中放置'using'指令通常是一个坏主意,因为该指令被应用于包含标题的任何文件。在你的情况下,任何包含pmt.h的文件(甚至是间接的)都会将所有名称空间std拉入全局名称空间。 – 2010-04-27 06:30:37

+0

我同意gareth。看到这里:http://stackoverflow.com/questions/2712076/为什么它是一个坏主意,'使用名称空间标准;(或任何其他,对于这个问题,根本。 – sbi 2010-04-27 06:38:25

回答

6

首先是一些分类。

class CPMT { 
    public: 
     double GetGain(); 
     // ... 
}; 

是定义一个类也没有限定所述成员函数。此

class CPMT { 
    public: 
     double GetGain() {return gain;} 
     // ... 
}; 

是定义相同的类,与还限定其成员函数(隐式地)内联。 This

double CPMT::GetGain() {return gain;} 
// ... 

正在定义成员函数(非内联)。

现在,如果你想从接口分离实现,你的头需要定义类,而你的实现文件需要定义它的成员函数。因此,纯粹的类定义

class CPMT { 
    public: 
     double GetGain(); 
     //... 
}; 

进入头文件和实现

double CPMT::GetGain() {return gain;} 
// ... 

进入执行文件 - 除了你想实现直列的成员函数。由于inline要求编译器在每次调用函数时都替换函数的实现,因此实现必须存在于函数被调用的地方。这就是为什么内联函数的实现必须在头文件中。

内联成员函数有两种方法。一种是在其类别的定义内定义它

class CPMT { 
    public: 
     double GetGain() {return gain;} 
     // ... 
}; 

它隐含地使它inline。另一种是明确内联它

class CPMT { 
    public: 
     double GetGain(); 
     //... 
}; 

inline double CPMT::GetGain() {return gain;} 
// ... 

在这两种情况下,实现必须在头文件中。

+1

内联函数的一个很好的链接是:http://www.parashift.com/c++-faq-li te/inline-functions.html – 2010-04-27 07:24:23

+0

+1:我同意。现在很多现代C++库只有头文件。我尽量在头文件中编写尽可能多的代码,并且只在必要时才放置实现。 – 2010-04-27 07:55:15

+0

@Eddy:我不同意。有理由让事情“内联”,并有理由反对这样做。 (对于初学者来说,很多现代C++库现在只是头文件,因为它们大部分的东西都是模板,它们都被放在头文件中。而且,_libraries_中的内联代码比_application code_要强得多。) – sbi 2010-04-27 07:58:59

4

您pmt.C文件的语法是错误的。它应该阅读

double CPMT::GetGain() {return gain;} 
double CPMT::GetVoltage() {return voltage;} 
void CPMT::SetGain(double g) {gain=g;} 
void CPMT::SetVoltage(double v) {voltage=v;} 
1

尝试重命名文件.CPP或.CXX,你的编译器可能会假设.C意味着它是C,而不是C++,这似乎是因为它没有重整的名字的情况下。

+0

谢谢你的建议,但我我一直在使用.C扩展了很长一段时间,似乎没什么问题,但这不是我的选择! – physicsmichael 2010-04-27 14:36:24

1

看起来像你正在编译和链接单独的.C文件,这些文件本身并不完整。您需要首先编译它们以获取.o文件,然后链接.o文件以获取最终的可执行文件。

g++ make_pmt.C pmt.C 

另外,pmt.C应该只在头文件中声明的函数定义:这一切都可以做到用。

6

pmt.C应该是这样的:

#include "pmt.h" 

using namespace std; 

double CPMT::GetGain() {return gain;} 
double CPMT::GetVoltage() {return voltage;} 

void CPMT::SetGain(double g) {gain=g;} 
void CPMT::SetVoltage(double v) {voltage=v;} 

我编译这样的:

g++ make_pmt.C pmt.C 

您还需要添加一个构造函数并初始化增益和电压。

+0

我读过了,如果你遗漏了一个构造函数,它会自动使用缺省的构造函数,没有提供参数。我只是没有初始化变量,因为它只是一个有启发性的例子,还是绝对有更多的东西比'双增益,电压'? – physicsmichael 2010-04-27 14:41:47

+0

它会产生一个默认构造函数,但增益和电压不会被初始化,包含垃圾 – 2010-04-27 21:45:43

4

在pmt.c中,您正在重新定义类。相反,你应该只定义其功能:

double CPMT::GetGain() { return gain; } 
double CPMT::GetVoltage() {return voltage;} 

void CPMT::SetGain(double g) {gain=g;} 
void CPMT::SetVoltage(double v) {voltage=v;} 

另外,你需要确保你提供这两个目标文件链接。

+0

CPMT ::应该在每个函数名之前 – 2010-04-27 06:32:34

+0

感谢Draco,你当然是对的 – 2010-04-27 06:44:53