physicsmichael
physicsmichael

Reputation: 4963

Splitting a C++ class into files now won't compile

I am teaching myself to write classes in C++ but can't seem to get the compilation to go through. If you can help me figure out not just how, but why, it would be greatly appreciated. Thank you in advance! Here are my three files:

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

And for reference, I get a linker error (right?):

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

Upvotes: 1

Views: 478

Answers (6)

sbi
sbi

Reputation: 224129

First some taxonomy.
This

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

is defining a class without also defining the member functions. This

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

is defining the same class, with also defining its member functions (implicitly) inline. This

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

is defining the member functions (not inline).

Now, if you want to separate implementation from interface, your header needs to define the class, while your implementation file needs to define its member functions. So the pure class definition

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

goes into the header file and the implementation

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

goes into the implementation file - except for those member functions you want to implement inline. Since inline asks the compiler to substitute a function's implementation for every call to it, the implementation must be present where the function is called. That's why the implementations of inlined functions must be in header files.

There are two ways to inline a member function. One is to define it within its class's definition

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

which implicitly makes it inline. The other is to explicitly inline it

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

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

In both cases the implementation must be in the header file.

Upvotes: 6

Alex Korban
Alex Korban

Reputation: 15126

In pmt.c, you're redefining the class. Instead, you should just define its functions:

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

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

Also, you need to make sure that you're providing both object files to the linker.

Upvotes: 4

Eddy Pronk
Eddy Pronk

Reputation: 6705

pmt.C would look like this:

#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;}

I compiled it like this:

g++ make_pmt.C pmt.C

You also need to add a constructor and initialize gain and voltage.

Upvotes: 6

codaddict
codaddict

Reputation: 455292

Looks like you are compiling and linking individual .C files which are not complete by themselves. You need to compile them first to get .o file and then link the .o files to get the final executable. This all can be done using:

g++ make_pmt.C pmt.C

Also the pmt.C should have only definition of the functions declared in the header file.

Upvotes: 1

Xorlev
Xorlev

Reputation: 8643

Try renaming the files .cpp or .cxx, your compiler may be assuming that .C means it is C and not C++, which seems to be the case since it isn't mangling the names.

Upvotes: 1

Gareth Stockwell
Gareth Stockwell

Reputation: 3122

The syntax of your pmt.C file is wrong. It should read

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

Upvotes: 4

Related Questions