A bit confused on Pure Virtual Functions

Let's say I make an abstract class with some pure virtual functions and derive 2 classes off of it. From my understanding the pure virtual functions are there in the abstract class as placeholders for the derived classes to sort-of fill in. However, when I go to compile the code without providing a function body for the one in the abstract class I get a Linker error. But when I do define the function for the abstract class it works. Why?
I thought that the whole point of pure virtual functions was to avoid the need of defining them in the base class. Am I mis-understanding some subtle point on virtual and pure virtual methods?
The point of pure virtual functions is to prevent instances of the abstract class. The functions themselves may have bodies (and if they are pure virtual destructors, they must have bodies).
Does it look something like this?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

using namespace std;

class PureAbstract {
 public:
	virtual void method() = 0;
};

class DerivedA : public PureAbstract {
 public:
	virtual void method() { cout << "Hello from A.\n"; }
};

class DerivedB : public PureAbstract {
 public:
	virtual void method() { cout << "Hello from B.\n"; }
};

//Test
int main() {
	DerivedA a;
	DerivedB b;

	PureAbstract &refA = a;
	refA.method();

	PureAbstract &refB = b;
	refB.method();
}
Last edited on
@Cubbi - other than virtual destructors, I thought other pure virtual functions did not have to have a body defined in the abstract class, but when I don't define the body for the function in the abstract class I get a linker error.

@Mathhead - Yes, except I would get an error for the PureAbstract method, because it doesn't have a body (which it shouldn't need, correct?).
The error I get is an LNK2019 error, in visual studio 2010. I'm not sure how much that helps though.
Well are you trying to invoke it? Something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
class PureAbstract {
 public:
	virtual void method() = 0;
};

class Derived : public PureAbstract {
 public:
	virtual void method() { PureAbstract::method(); }
};

int main() {
	Derived d; //this line causes a LNK2019 error in visual studios for me
}
Last edited on
@Keith89 - Mathhead's program compiles and runs with Visual Studio 2010 with no errors. Post your code.
Hmm..I don't think I am. Like what I've done is create a PureAbstract pointer that points to an array. Then I made parts of the array AbstractDerived1 and other parts AbstractDerived2 (all are derived objects) and invoke the methods after that. All the elements are initialized by the time I use the array to access objects' methods and such. So it shouldn't be invoking the PureAbstract.

Edit: I will post the code..
Last edited on
Like this?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

using namespace std;

class PureAbstract {
 public:
	virtual void method() = 0;
};

class DerivedA : public PureAbstract {
 public:
	virtual void method() { cout << "Hello from A.\n"; }
};

class DerivedB : public PureAbstract {
 public:
	virtual void method() { cout << "Hello from B.\n"; }
};

//Test
int main() {
	PureAbstract arr[3] = { DerivedA(), DerivedB(), DerivedA() };
	for( unsigned int i = 0; i < 3; ++i )
		arr[i].method();
}
error LNK2019: unresolved external symbol "public: virtual void __thiscall PureAbstract::method(void)" (?method@PureAbstract@@UAEXXZ) referenced in function _main
polymorphism.exe : fatal error LNK1120: 1 unresolved externals
If you're trying to invoke it, then of course it needs a body, otherwise there's nothing to invoke:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
class PureAbstract {
 public:
    virtual void method() = 0;
};
void PureAbstract::method() { std::cout << "Hi from abstract\n"; }

class Derived : public PureAbstract {
 public:
    virtual void method() { PureAbstract::method(); }
};

int main() {
    Derived d;
    d.method();
}
demo: http://ideone.com/9Z5aI
Okay, I think I see why it wouldn't work now. I had scanned over the code a bit too quickly and missed a function call for a function that hadn't been defined. This code runs, I'm still going to post it and would appreciate validation of the first couple comments I made in the definition files. Thank you for all the help.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//Header File
// acctabc.h -- bank account classes
#ifndef ACCTABC_H_
#define ACCTABC_H_

// Abstract Base Class
class AcctABC
{
private:
	enum {MAX = 35};
	char fullName[MAX];
	long acctNum;
	double balance;
protected:
	const char * FullName() const {return fullName;}
	long AcctNum() const {return acctNum;}
	std::ios_base::fmtflags SetFormat() const;
public:
	AcctABC(const char *s = "Nullbody", long an = -1,
				double bal = 0.0);
	void Deposit(double amt);
	virtual void Withdraw(double amt) = 0;	// pure virtual function
	double Balance() const {return balance;}
	virtual void ViewAcct() const = 0;		// pure virtual function
	virtual ~AcctABC() {}
};

// Brass Account Class
class Brass :public AcctABC
{
public:
	Brass(const char *s = "Nullbody", long an = -1,
		double bal = 0.0) : AcctABC(s, an, bal) { }
	virtual void Withdraw(double amt);
	virtual void ViewAcct() const;
	virtual ~Brass() {}
};

// Brass Plus Account Class
class BrassPlus : public AcctABC
{
private:
	double maxLoan;
	double rate;
	double owesBank;
public:
	BrassPlus(const char *s = "Nullbody", long an = -1, 
			double bal = 0.0, double ml = 500,
			double r = 0.10);
	BrassPlus(const Brass & ba, double ml = 500, double r = 0.1);
	virtual void ViewAcct() const;
	virtual void Withdraw(double amt);
	void ResetMax(double m) {maxLoan = m; }
	void ResetRate(double r) { rate = r;}
	void ResetOwes() { owesBank = 0;}
};

#endif 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//method definitions
// acctabc.cpp -- bank account class methods
#include <iostream>
#include <cstring>
using std::cout;
using std::ios_base;
using std::endl;

#include "acctabc.h"

// Abstract Base Class
AcctABC::AcctABC(const char *s, long an, double bal)
{                                                                                          // Constructor is still needed with    
                                                                                                //abstract class.
	std::strncpy(fullName, s, MAX - 1);
	fullName[MAX - 1] = '\0';
	acctNum = an;
	balance = bal;
}

void AcctABC::Deposit(double amt)
{
if (amt < 0)                                                   //The comment below applies to this function to.
cout << "Negative deposit not allowed; "
<< "deposit is cancelled.\n";
else 
balance += amt;
}

void AcctABC::Withdraw(double amt)
{
balance -= amt;                                       /* from what I understand, the only reason that this
                                                                   is used often.  But this definition is not necessary as
                                                                  long as the functions that need it are re-done.right?*/
}

// protected method
ios_base::fmtflags AcctABC::SetFormat() const
{
// set up ###.## format
	ios_base::fmtflags initialState =
		cout.setf(ios_base::fixed, ios_base::floatfield);
	cout.setf(ios_base::showpoint);
	cout.precision(2);
	return initialState;
}

// Brass methods
void Brass::Withdraw(double amt)
{
	if (amt < 0)
		cout << "Withdrawal amount must be positive; "
		<< "withdrawal cancelled.\n";
	else if (amt <= Balance())
		AcctABC::Withdraw(amt);			// This is the function call I missed
	else 
		cout << "Withdrawal amount of $" << amt
			 << " exceeds your balance.\n"
			 << "Withdrawal cancelled.\n";
}

void Brass::ViewAcct() const 
{
	ios_base::fmtflags initialState = SetFormat();
	cout << "Brass Client: " << FullName() << endl;
	cout << "Account Number: " << AcctNum() << endl;
	// rest is edited out to save screen space on post...all functions do get defined though. 
Last edited on
Topic archived. No new replies allowed.