This article explores the design and implementation of several C++ classes, focusing on encapsulation, constructors, destructors, static members, and operator overloading.
Task 1: Class T
The following code defines a simple class T with basic member functions and constructors.
#include "ClassT.h"
#include <iostream>
#include <string>
const std::string ClassT::description{"Example class T"};
const int ClassT::max_count = 999;
int ClassT::instance_count = 0;
int ClassT::getInstanceCount() {
return instance_count;
}
ClassT::ClassT(int x, int y) : value1{x}, value2{y} {
++instance_count;
std::cout << "ClassT constructor invoked.\n";
}
ClassT::ClassT(const ClassT &obj) : value1{obj.value1}, value2{obj.value2} {
++instance_count;
std::cout << "ClassT copy constructor invoked.\n";
}
ClassT::ClassT(ClassT &&obj) : value1{obj.value1}, value2{obj.value2} {
++instance_count;
std::cout << "ClassT move constructor invoked.\n";
}
ClassT::~ClassT() {
--instance_count;
std::cout << "ClassT destructor invoked.\n";
}
void ClassT::modify(int factor) {
value1 *= factor;
value2 *= factor;
}
void ClassT::show() const {
std::cout << "(" << value1 << ", " << value2 << ")";
}
void utilityFunction() {
ClassT obj5(42);
obj5.value2 = 2049;
std::cout << "obj5 = "; obj5.show(); std::cout << '\n';
}
The header file for this class includes:
#pragma once
#include <string>
class ClassT {
public:
ClassT(int x = 0, int y = 0);
ClassT(const ClassT &obj);
ClassT(ClassT &&obj);
~ClassT();
void modify(int factor);
void show() const;
private:
int value1, value2;
public:
static int getInstanceCount();
public:
static const std::string description;
static const int max_count;
private:
static int instance_count;
friend void utilityFunction();
};
void utilityFunction();
The main functon demonstrates creating and manipulating instances of ClassT:
#include "ClassT.h"
#include <iostream>
void testClassT();
int main() {
std::cout << "Testing ClassT:\n";
testClassT();
std::cout << "\nTesting friend function:\n";
utilityFunction();
}
void testClassT() {
using std::cout;
using std::endl;
cout << "ClassT info: " << ClassT::description << endl;
cout << "Max number of ClassT objects: " << ClassT::max_count << endl;
cout << "Current number of ClassT objects: " << ClassT::getInstanceCount() << endl << endl;
ClassT obj1;
cout << "obj1 = "; obj1.show(); cout << endl;
ClassT obj2(3, 4);
cout << "obj2 = "; obj2.show(); cout << endl;
ClassT obj3(obj2);
obj3.modify(2);
cout << "obj3 = "; obj3.show(); cout << endl;
ClassT obj4(std::move(obj2));
cout << "obj4 = "; obj4.show(); cout << endl;
cout << "Test: Current number of ClassT objects: " << ClassT::getInstanceCount() << endl;
}
Task 2: Complex Number Class
The following code defines a ComplexNumber class that supports basic operations.
#include "ComplexNumber.h"
#include <iostream>
#include <cmath>
const std::string ComplexNumber::documentation = "Simplified complex number class";
ComplexNumber::ComplexNumber() : realPart(0.0), imaginaryPart(0.0) {}
ComplexNumber::ComplexNumber(double real) : realPart(real), imaginaryPart(0.0) {}
ComplexNumber::ComplexNumber(double real, double imag) : realPart(real), imaginaryPart(imag) {}
ComplexNumber::ComplexNumber(const ComplexNumber& other) : realPart(other.realPart), imaginaryPart(other.imaginaryPart) {}
double ComplexNumber::getReal() const {
return realPart;
}
double ComplexNumber::getImaginary() const {
return imaginaryPart;
}
void ComplexNumber::add(const ComplexNumber& other) {
realPart += other.realPart;
imaginaryPart += other.imaginaryPart;
}
void print(const ComplexNumber& c) {
std::cout << c.realPart;
if (c.imaginaryPart >= 0) {
std::cout << " + " << c.imaginaryPart << "i";
} else {
std::cout << " - " << -c.imaginaryPart << "i";
}
}
double magnitude(const ComplexNumber& c) {
return std::sqrt(c.realPart * c.realPart + c.imaginaryPart * c.imaginaryPart);
}
ComplexNumber sum(const ComplexNumber& c1, const ComplexNumber& c2) {
return ComplexNumber(c1.realPart + c2.realPart, c1.imaginaryPart + c2.imaginaryPart);
}
bool isEqual(const ComplexNumber& c1, const ComplexNumber& c2) {
return (c1.realPart == c2.realPart) && (c1.imaginaryPart == c2.imaginaryPart);
}
bool isNotEqual(const ComplexNumber& c1, const ComplexNumber& c2) {
return !isEqual(c1, c2);
}
The header file for the ComplexNumber class is:
#ifndef COMPLEXNUMBER_H
#define COMPLEXNUMBER_H
#include <string>
class ComplexNumber {
public:
static const std::string documentation;
ComplexNumber();
ComplexNumber(double real);
ComplexNumber(double real, double imag);
ComplexNumber(const ComplexNumber& other);
double getReal() const;
double getImaginary() const;
void add(const ComplexNumber& other);
friend void print(const ComplexNumber& c);
friend double magnitude(const ComplexNumber& c);
friend ComplexNumber sum(const ComplexNumber& c1, const ComplexNumber& c2);
friend bool isEqual(const ComplexNumber& c1, const ComplexNumber& c2);
friend bool isNotEqual(const ComplexNumber& c1, const ComplexNumber& c2);
private:
double realPart;
double imaginaryPart;
};
void print(const ComplexNumber& c);
double magnitude(const ComplexNumber& c);
ComplexNumber sum(const ComplexNumber& c1, const ComplexNumber& c2);
bool isEqual(const ComplexNumber& c1, const ComplexNumber& c2);
bool isNotEqual(const ComplexNumber& c1, const ComplexNumber& c2);
#endif
The main function tests both the custom ComplexNumber class and the stendard library std::complex class:
#include <iostream>
#include <iomanip>
#include <complex>
#include "ComplexNumber.h"
void testCustomComplex();
void testStdComplex();
int main() {
std::cout << "***** Test 1: Custom ComplexNumber class *****\n";
testCustomComplex();
std::cout << "\n***** Test 2: Standard library complex class *****\n";
testStdComplex();
}
void testCustomComplex() {
using std::cout;
using std::endl;
using std::boolalpha;
cout << "Class member test:\n";
cout << ComplexNumber::documentation << endl << endl;
cout << "ComplexNumber object test:\n";
ComplexNumber cn1;
ComplexNumber cn2(3, -4);
ComplexNumber cn3(cn2);
ComplexNumber cn4 = cn2;
const ComplexNumber cn5(3.5);
cout << "cn1 = "; print(cn1); cout << endl;
cout << "cn2 = "; print(cn2); cout << endl;
cout << "cn3 = "; print(cn3); cout << endl;
cout << "cn4 = "; print(cn4); cout << endl;
cout << "cn5.real = " << cn5.getReal()
<< ", cn5.imag = " << cn5.getImaginary() << endl << endl;
cout << "Complex number operation test:\n";
cout << "magnitude(cn2) = " << magnitude(cn2) << endl;
cn1.add(cn2);
cout << "cn1 += cn2, cn1 = "; print(cn1); cout << endl;
cout << boolalpha;
cout << "cn1 == cn2 : " << isEqual(cn1, cn2) << endl;
cout << "cn1 != cn2 : " << isNotEqual(cn1, cn2) << endl;
cn4 = sum(cn2, cn3);
cout << "cn4 = cn2 + cn3, cn4 = "; print(cn4); cout << endl;
}
void testStdComplex() {
using std::cout;
using std::endl;
using std::boolalpha;
cout << "std::complex<double> object test:\n";
std::complex<double> cn1;
std::complex<double> cn2(3, -4);
std::complex<double> cn3(cn2);
std::complex<double> cn4 = cn2;
const std::complex<double> cn5(3.5);
cout << "cn1 = " << cn1 << endl;
cout << "cn2 = " << cn2 << endl;
cout << "cn3 = " << cn3 << endl;
cout << "cn4 = " << cn4 << endl;
cout << "cn5.real = " << cn5.real()
<< ", cn5.imag = " << cn5.imag() << endl << endl;
cout << "Complex number operation test:\n";
cout << "magnitude(cn2) = " << std::abs(cn2) << endl;
cn1 += cn2;
cout << "cn1 += cn2, cn1 = " << cn1 << endl;
cout << boolalpha;
cout << "cn1 == cn2 : " << (cn1 == cn2)<< endl;
cout << "cn1 != cn2 : " << (cn1 != cn2) << endl;
cn4 = cn2 + cn3;
cout << "cn4 = cn2 + cn3, cn4 = " << cn4 << endl;
}
Task 3: Player Control Class
This task involves a MusicController class that parses and executes player commands.
#include "MusicController.h"
#include <iostream>
#include <algorithm>
int MusicController::totalCommands = 0;
MusicController::MusicController() {}
std::string toLowerCase(const std::string& input) {
std::string result;
for (char c : input) {
result += std::tolower(static_cast<unsigned char>(c));
}
return result;
}
CommandType MusicController::interpret(const std::string& commandStr) {
std::string lowerCmd = toLowerCase(commandStr);
CommandType cmd = CommandType::Unknown;
if (lowerCmd == "play") {
cmd = CommandType::Play;
} else if (lowerCmd == "pause") {
cmd = CommandType::Pause;
} else if (lowerCmd == "next") {
cmd = CommandType::Next;
} else if (lowerCmd == "prev") {
cmd = CommandType::Previous;
} else if (lowerCmd == "stop") {
cmd = CommandType::Stop;
}
if (cmd != CommandType::Unknown) {
totalCommands++;
}
return cmd;
}
void MusicController::perform(CommandType action) const {
switch (action) {
case CommandType::Play: std::cout << "[Play] Playing music...\n"; break;
case CommandType::Pause: std::cout << "[Pause] Music paused\n"; break;
case CommandType::Next: std::cout << "[Next] Skipping to next track\n"; break;
case CommandType::Previous: std::cout << "[Previous] Back to previous track\n"; break;
case CommandType::Stop: std::cout << "[Stop] Music stopped\n"; break;
default: std::cout << "[Error] Unknown command\n"; break;
}
}
int MusicController::getCommandCount() {
return totalCommands;
}
The header file for the MusicController class:
#pragma once
#include <string>
enum class CommandType { Play, Pause, Next, Previous, Stop, Unknown };
class MusicController {
public:
MusicController();
static int getCommandCount();
CommandType interpret(const std::string& commandStr);
void perform(CommandType action) const;
private:
static int totalCommands;
};
The main function allows user interaction with the music controller:
#include "MusicController.h"
#include <iostream>
void runTests() {
MusicController controller;
std::string commandStr;
std::cout << "Enter command: (play/pause/next/prev/stop/quit):\n";
while (std::cin >> commandStr) {
if (commandStr == "quit")
break;
CommandType cmd = controller.interpret(commandStr);
controller.perform(cmd);
std::cout << "Total commands processed: " << MusicController::getCommandCount() << "\n\n";
}
}
int main() {
runTests();
}
Task 4: Fraction Class
This task focuses on a RationalNumber class that handles arithmetic operations with fractions.
#include "RationalNumber.h"
#include <iostream>
#include <stdexcept>
#include <cmath>
const std::string RationalNumber::description = "RationalNumber class v 0.01\nSupports construction, output, addition, subtraction, multiplication, and division.";
int RationalNumber::greatestCommonDivisor(int a, int b) {
a = std::abs(a);
b = std::abs(b);
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
void RationalNumber::reduce() {
if (denominator == 0)
return;
if (denominator < 0) {
numerator = -numerator;
denominator = -denominator;
}
int divisor = greatestCommonDivisor(numerator, denominator);
if (divisor != 0) {
numerator /= divisor;
denominator /= divisor;
}
}
RationalNumber::RationalNumber(int num, int denom) : numerator(num), denominator(denom) {
if (denominator == 0)
return;
reduce();
}
RationalNumber::RationalNumber(const RationalNumber& other) : numerator(other.numerator), denominator(other.denominator) {}
int RationalNumber::getNumerator() const {
return numerator;
}
int RationalNumber::getDenominator() const {
return denominator;
}
RationalNumber RationalNumber::negate() const {
return RationalNumber(-numerator, denominator);
}
void display(const RationalNumber& frac) {
if (frac.getDenominator() == 0) {
std::cout << "Denominator cannot be zero";
return;
}
int num = frac.getNumerator();
int denom = frac.getDenominator();
if (denom == 1) {
std::cout << num;
} else if (num == 0) {
std::cout << "0";
} else {
std::cout << num << "/" << denom;
}
}
RationalNumber addFractions(const RationalNumber& frac1, const RationalNumber& frac2) {
if (frac1.getDenominator() == 0 || frac2.getDenominator() == 0)
return RationalNumber(0, 0);
int newNum = frac1.getNumerator() * frac2.getDenominator() + frac2.getNumerator() * frac1.getDenominator();
int newDenom = frac1.getDenominator() * frac2.getDenominator();
return RationalNumber(newNum, newDenom);
}
RationalNumber subtractFractions(const RationalNumber& frac1, const RationalNumber& frac2) {
if (frac1.getDenominator() == 0 || frac2.getDenominator() == 0)
return RationalNumber(0, 0);
int newNum = frac1.getNumerator() * frac2.getDenominator() - frac2.getNumerator() * frac1.getDenominator();
int newDenom = frac1.getDenominator() * frac2.getDenominator();
return RationalNumber(newNum, newDenom);
}
RationalNumber multiplyFractions(const RationalNumber& frac1, const RationalNumber& frac2) {
if (frac1.getDenominator() == 0 || frac2.getDenominator() == 0)
return RationalNumber(0, 0);
int newNum = frac1.getNumerator() * frac2.getNumerator();
int newDenom = frac1.getDenominator() * frac2.getDenominator();
return RationalNumber(newNum, newDenom);
}
RationalNumber divideFractions(const RationalNumber& frac1, const RationalNumber& frac2) {
if (frac1.getDenominator() == 0 || frac2.getDenominator() == 0 || frac2.getNumerator() == 0)
return RationalNumber(0, 0);
int newNum = frac1.getNumerator() * frac2.getDenominator();
int newDenom = frac1.getDenominator() * frac2.getNumerator();
return RationalNumber(newNum, newDenom);
}
The header file for the RationalNumber class:
#ifndef RATIONALNUMBER_H
#define RATIONALNUMBER_H
#include <string>
class RationalNumber {
public:
static const std::string description;
RationalNumber(int num = 0, int denom = 1);
RationalNumber(const RationalNumber& other);
int getNumerator() const;
int getDenominator() const;
RationalNumber negate() const;
private:
int numerator;
int denominator;
void reduce();
static int greatestCommonDivisor(int a, int b);
};
void display(const RationalNumber& frac);
RationalNumber addFractions(const RationalNumber& frac1, const RationalNumber& frac2);
RationalNumber subtractFractions(const RationalNumber& frac1, const RationalNumber& frac2);
RationalNumber multiplyFractions(const RationalNumber& frac1, const RationalNumber& frac2);
RationalNumber divideFractions(const RationalNumber& frac1, const RationalNumber& frac2);
#endif
The main function tests the RationalNumber class functionality:
#include "RationalNumber.h"
#include <iostream>
void testBasicFunctionality();
void testZeroDenominator();
int main() {
std::cout << "Test 1: RationalNumber class basic functionality\n";
testBasicFunctionality();
std::cout << "\nTest 2: Zero denominator test:\n";
testZeroDenominator();
}
void testBasicFunctionality() {
using std::cout;
using std::endl;
cout << "RationalNumber class test:\n";
cout << RationalNumber::description << endl << endl;
RationalNumber r1(5);
RationalNumber r2(3, -4), r3(-18, 12);
RationalNumber r4(r3);
cout << "r1 = "; display(r1); cout << endl;
cout << "r2 = "; display(r2); cout << endl;
cout << "r3 = "; display(r3); cout << endl;
cout << "r4 = "; display(r4); cout << endl;
const RationalNumber r5(r4.negate());
cout << "r5 = "; display(r5); cout << endl;
cout << "r5.getNumerator() = " << r5.getNumerator()
<< ", r5.getDenominator() = " << r5.getDenominator() << endl;
cout << "r1 + r2 = "; display(addFractions(r1, r2)); cout << endl;
cout << "r1 - r2 = "; display(subtractFractions(r1, r2)); cout << endl;
cout << "r1 * r2 = "; display(multiplyFractions(r1, r2)); cout << endl;
cout << "r1 / r2 = "; display(divideFractions(r1, r2)); cout << endl;
cout << "r4 + r5 = "; display(addFractions(r4, r5)); cout << endl;
}
void testZeroDenominator() {
using std::cout;
using std::endl;
RationalNumber r6(42, 55), r7(0, 3);
cout << "r6 = "; display(r6); cout << endl;
cout << "r7 = "; display(r7); cout << endl;
cout << "r6 / r7 = "; display(divideFractions(r6, r7)); cout << endl;
}