Exploring C++
C++ is a general-purpose, middle-level language originally developed by Danish computer scientist, Bjarne Stroustrup, in 1979 at Bell Laboratories USA. It was designed as an extension of the C language, adding features such as classes, inheritance, abstraction, and polymorphism.
C++ can be traced back to 1979 when Bjarne Stroustrup was working on his Ph.D. thesis at Bell Labs. He aimed to create a language that combined low-level capabilities with high-level abstractions. This was influenced by his experience with a language called Simula, which was designed for simulations but too slow for practical use.
Initially known as "C with classes," C++ was developed as a superset of the C language. Stroustrup's goal was to bring object-oriented programming to C without sacrificing speed or low-level functionality. The first C++ compiler, called Cfront, was created to translate C++ code into universal C.
In 1983, the language's name was changed to C++. The "++" operator in C signifies incrementing a variable, reflecting Stroustrup's view of improving upon C. This period saw the introduction of significant features like virtual functions, function overloading, references with the const keyword, and single-line comments.
In 1985, Stroustrup published "The C++ Programming Language," and C++ was established as a commercial product. The language continued to evolve with the addition of features like protected and static members and inheritance from multiple classes. In 1990, "The Annotated C++ Reference Manual" was released.
In 1998, the first international standard for C++, known as C++98, was published. Subsequent revisions included C++03 and C++11, the latter introducing many new features. C++14 and C++17 followed with more enhancements, and C++20 extended the language's capabilities further.
C++ is widely used in various industries for systems programming, gaming, finance, scientific computing, and robotics. It offers low-level access to hardware resources, making it suitable for system-level software and high-performance numerical libraries.
C++ excels in building complex software systems where performance, efficiency, and reliability are crucial. It is popular for developing device drivers, operating systems, and embedded systems. The language's support for scientific computing and simulation tools is also notable.
C++ is statically typed, while Java is dynamically typed. Memory management in C++ is manual, whereas Java relies on the JVM for garbage collection. C++ is generally faster but less portable than Java. Java has a larger ecosystem of libraries.
Feature | C++ | Java |
---|---|---|
Compiled or Interpreted | Compiled | Compiled (but bytecode executed by JVM) |
Memory Management | Manual (with pointers) | Automatic (garbage collected) |
Performance | High performance with direct memory access | Good performance with JIT compilation |
Platform Independence | Platform-dependent | Platform-independent (runs on JVM) |
Community and Libraries | Large community and extensive libraries | Large community and rich standard libraries |
Language Paradigm | Multi-paradigm (Associated with procedural, object-oriented, and generic programming) | Primarily Object-Oriented |
- | - | - |
C++ has a complex syntax and is statically typed, while Python has a simpler syntax and is dynamically typed. Python is highly portable and interpreted, while C++ needs separate compilation for each platform. C++ is faster but requires manual memory management, while Python manages memory automatically.
Feature | C++ | Python |
---|---|---|
Typing | Statically typed | Dynamically typed |
Syntax | Complex syntax | Simple syntax |
Portability | Platform-dependent (needs compilation) | Platform-independent (interpreted) |
Memory Management | Manual memory management | Automatic memory management (garbage collected) |
- | - | - |
C++ is an extension of the C programming language and shares many similarities with it. However, there are also some key differences:
Feature | C++ | C |
---|---|---|
Paradigm | Multi-paradigm (supports procedural, object-oriented, and generic programming) | Procedural |
Abstraction | Supports high-level abstractions with classes and objects | Low-level language with limited support for abstractions |
Memory Management | Supports both manual and automatic memory management (with features like RAII) | Manual memory management (malloc and free functions) |
Code Reusability | Encourages code reusability through object-oriented principles | Code reuse is limited to functions and libraries |
Performance | Can achieve high performance with optimizations, but may have some overhead due to abstractions | Offers fine-grained control over performance but lacks high-level abstractions |
- | - | - |
In conclusion, the history of C++ is both fascinating and influential. From its early days as "C with classes" to its current status as a widely-used programming language, C++ has undergone significant evolution and development.
C++ stands out as a versatile and powerful tool in the world of software development. Its combination of flexibility, efficiency, and support for object-oriented programming makes it well-suited for a wide range of applications. From system-level programming to gaming, finance, scientific computing, and robotics, C++ continues to play a vital role in various industries.
While C++ may pose a learning curve for novice programmers due to its complex syntax and manual memory management, it rewards those who master it with the ability to create high-performance, efficient, and reliable software systems.
Looking ahead, C++ remains a relevant and important language in today's programming landscape. With ongoing updates and new features introduced in each version, C++ continues to evolve to meet the changing demands of modern software development. Its legacy is secured, and its future looks promising as it adapts to new challenges and technologies.
Comprehensive documentation resource for the C++ programming language. Includes detailed information about C++ syntax, standard libraries, and usage.
Official website for the C++ programming language. It offers a range of resources, including tutorials, articles, forums, and references related to C++.
CPPReference is a well-known online reference for the C++ programming language. It provides detailed documentation on C++ standard libraries, classes, functions, and other language features.
This link likely directs you to the official website for the GNU Compiler Collection (GCC). GCC is a popular compiler for C and C++ programming languages. The website provides downloads, documentation, and resources related to GCC.
This link likely directs you to the C++ language documentation from Microsoft that aims to help you learn to use C++ and the C++ standard library.
These links are great references to learn C++.
Here are some options for getting started with C++:
If you prefer to work on your local machine, you can download and install a C++ compiler like GCC. Here are the steps:
If you want a quick and hassle-free way to start coding in C++, you can use an online compiler. We recommend the Programiz Online C++ Compiler. Here's how to get started:
Section that contains small examples
CarterHere's a simple "Hello, World!" program in C++:
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
===== OR =====
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World";
return 0;
}
To compile and run the program:
g++ HelloWorld.cpp -o HelloWorld
./HelloWorld
Here is a simple palindrome example in C++:
#include <iostream>
#include <string>
#include <cctype>
bool isPalindrome(const std::string& str) {
int start = 0;
int end = str.length() - 1;
while (start < end) {
while (start < end && !isalpha(str[start])) start++;
while (start < end && !isalpha(str[end])) end--;
if (tolower(str[start]) != tolower(str[end])) {
return false;
}
start++;
end--;
}
return true;
}
int main() {
std::string input;
std::cout << "Enter a String: ";
std::getline(std::cin, input);
if (isPalindrome(input)) {
std::cout << "The String is a palindrome.\n";
} else {
std::cout << "The String is not a palindrome.\n";
}
return 0;
}
To compile and run the program:
g++ palindrome_checker.cpp -o palindrome_checker
./palindrom_checker
Here are some more complex C++ examples:
MikeHere is a simple example of using multiple inheritance in C++:
#include <iostream>
#include <string>
// Define BodySoap Class
class BodySoap {
public:
void clean() {
std::cout << "Cleaning your body with body soap." << std::endl;
}
};
// Define Shampoo Class
class Shampoo {
public:
void clean() {
std::cout << "Cleaning your hair with shampoo." << std::endl;
}
};
// Define Conditioner Class
class Conditioner {
public:
void clean() {
std::cout << "Softening your hair with conditioner." << std::endl;
}
};
//Class Template for combinedProduct
template <typename T1, typename T2>
class combinedProduct : public T1, public T2{
public:
void useProduct() {
T1::clean();
T2::clean();
std::cout << "Using combined product for a full shower routine." << std::endl;
}
};
int main() {
combinedProduct<BodySoap, Shampoo> myProduct;
myProduct.useProduct(); // Using the combined functionality
combinedProduct<Conditioner, BodySoap> myOtherProduct;
myOtherProduct.useProduct();
return 0;
}
To compile and run the program:
g++ inheritance_soap.cpp -o inheritanceSoap
./inheritanceSoap
Every C++ expression has a type, and belongs to a value category. C++17 defines the expression value categories as: glvalue, prvalue, xvalue, lvalue, and rvalue.
#include <iostream>
#include <utility>
// This is how Microsoft defines the value categories:
// glvalue: An expression identifying an object, bit-field, or function.
// prvalue: An expression used to initialize an object or compute a value. (has no address accessible by program)
// xvalue: A glvalue that represents an object whose resources can be reused. (has an address that is no longer accessible by program but can be used to initialize an rvalue reference)
// lvalue: A glvalue that is not an xvalue. (has an address accessible by program)
// rvalue: Either a prvalue (temporary value) or an xvalue (reusable resource).
int main() {
// glvalue example (lvalue)
int x = 5; // 'x' is an lvalue because it is a named variable that refers to a specific location in memory.
std::cout << "lvalue (x): " << x << std::endl;
// glvalue example (xvalue)
int&& x_rvalue_ref = std::move(x); // 'std::move(x)' produces an xvalue, indicating 'x' is ready to be moved.
std::cout << "xvalue (std::move(x)): " << x_rvalue_ref << std::endl;
// rvalue example (xvalue)
int z = std::move(x_rvalue_ref); // 'std::move(x_rvalue_ref)' is an rvalue (xvalue), ready for moving.
std::cout << "rvalue (std::move(x_rvalue_ref)): " << z << std::endl;
// rvalue example (prvalue)
int i = 12; // '12' is an rvalue (prvalue), a temporary value not associated with a specific memory location.
std::cout << "rvalue (12): " << i << std::endl;
return 0;
}
To compile and run the program:
g++ value_categories.cpp -o valueCategories
./valueCategories
This example shows Tic Tac Toe in C++ working within an Online Compiler
#include <iostream>
#include <vector>
using namespace std;
// Function to display the Tic-Tac-Toe board
void displayBoard(const vector<vector<char>>& board) {
// Loop through rows
for (int i = 0; i < 3; i++) {
// Loop through columns within each row
for (int j = 0; j < 3; j++) {
// Output the symbol at the current position in the board
cout << board[i][j];
// Add a separator "|" if not at the last column in the row
if (j < 2) cout << " | ";
}
// Move to the next line after completing each row
cout << endl;
// Add a horizontal line "---------" if not at the last row
if (i < 2) cout << "---------" << endl;
}
}
// Function to check if a player has won
bool checkWin(const vector<vector<char>>& board, char player) {
for (int i = 0; i < 3; i++) {
// Check rows and columns using loops
if ((board[i][0] == player && board[i][1] == player && board[i][2] == player) ||
(board[0][i] == player && board[1][i] == player && board[2][i] == player)) {
return true;
}
}
// Check diagonals
if ((board[0][0] == player && board[1][1] == player && board[2][2] == player) ||
(board[0][2] == player && board[1][1] == player && board[2][0] == player)) {
return true;
}
return false;
}
// Function to check if the board is full (a tie)
bool isBoardFull(const vector<vector<char>>& board) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// If an empty cell is found, the board is not full
if (board[i][j] == ' ') {
return false;
}
}
}
// If no empty cells are found, the board is full
return true;
}
int main() {
// Initialize an empty 3x3 board using vectors
vector<vector<char>> board(3, vector(3, ' '));
char currentPlayer = 'X';
cout << "Tic-Tac-Toe Game" << endl; // Display the message "Tic-Tac-Toe Game"
while (true) {
displayBoard(board);
// Player's move
int row, col;
// Prompt the current player to enter their move (row and column)
cout << "Player " << currentPlayer << ", enter your move (row and column): ";
cin >> row >> col;
// Check if the move is valid (within the board and the cell is not occupied)
if (row < 1 || row > 3 || col < 1 || col > 3 || board[row - 1][col - 1] != ' ') {
cout << "Invalid move! Try again." << endl;
continue;
}
// Make the move by updating the board with the player's symbol
board[row - 1][col - 1] = currentPlayer;
// Check if the current player wins by calling the checkWin function
if (checkWin(board, currentPlayer)) {
displayBoard(board);
cout << "Player " << currentPlayer << " wins! Congratulations!" << endl;
break;
}
// Check for a tie (all cells are occupied) by calling the isBoardFull function
if (isBoardFull(board)) {
displayBoard(board);
cout << "It's a tie! The game ends in a draw." << endl;
break;
}
// Switch to the other player for the next turn (X -> O or O -> X)
currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}
return 0;
}
To compile and run the program:
g++ TicTacToe.cpp -o TicTacToe
./TicTacToe
Copy and Paste Code into Online Compiler:
Programiz Online C++ CompilerAddressing a Complex Challenges with C++
Our team has taken on the task of solving a multitude of complex problems using the C++ programming language. One of the problems we are addressing are taking the Java banking application we've seen in our previous homeworks and converting it to C++. The second problem we addressed was the Double Ended Queue Ported from C.
This example shows the C++ version of the Java banking application we've seen in our previous homeworks.
In C++, header (`.h`) and source (`.cpp`) files are used to organize our code. Header files are typically used for declaration and source files are usually used for the implementation/definition. In this banking application the Customer, CheckingAccount, and SavingAccount classes are simple enough to be fully defined in their header files. Also, a function defined in the body of a class declaration is implicitly an inline function. Regular function calling can cause overhead when the program control is transfered to the called function and back to the main program. Inline functions elimate these extra steps by inserting the function's code directly into the caller's code, which can make them more efficient.
The Account and Bank classes have their own source files as their toString methods are more complex so they should be implemented in the source file, not the header file. Then main.cpp is used as the main method that ties the whole application together.
One especially cool feature of this C++ version is the use of smart pointers for managing memory.
Smart pointers help ensure that the memory used by the program is handled properly. For example, a
std::unique_ptr
makes sure only one pointer points to that piece of memory at a time. Once this pointer is done with
the
memory,
it automatically frees it up for us. This makes it a lot more simple then using manual pointers and
manually freeing memory.
//include guards prevent multiple inclusions
#ifndef CUSTOMER_H
#define CUSTOMER_H
#include <string>
class Customer {
private:
std::string name;
public:
Customer(std::string n) : name(n) {}
std::string toString() const { return name; }
};
#endif // CUSTOMER_H
#ifndef CHECKINGACCOUNT_H
#define CHECKINGACCOUNT_H
#include <memory>
#include "Account.h"
//inherits from Account class
class CheckingAccount : public Account {
public:
CheckingAccount(std::string number, std::shared_ptr<Customer> customer, double balance)
: Account(number, customer, balance) {}
//override Account accrue function
void accrue(double rate) override {}
};
#endif // CHECKINGACCOUNT_H
#ifndef SAVINGACCOUNT_H
#define SAVINGACCOUNT_H
#include <memory>
#include "Account.h"
class SavingAccount : public Account {
private:
double interest = 0;
public:
SavingAccount(std::string number, std::shared_ptr<Customer> customer, double balance)
: Account(number, customer, balance) {}
//override Account accrue function
void accrue(double rate) override {
interest += balance * rate;
balance += balance * rate;
}
};
#endif // SAVINGACCOUNT_H
#ifndef ACCOUNT_H
#define ACCOUNT_H
#include <string>
#include <memory>
// Forward declaration
class Customer;
//abstract class since it has at least one pure virtual function
class Account {
protected:
std::string number;
std::shared_ptr<Customer> customer; // Shared pointer for Customer
double balance;
public:
Account(std::string num, std::shared_ptr<Customer> c, double b)
: number(num), customer(c), balance(b) {}
//pure virtual function. must be implemented in derived classes
virtual void accrue(double rate) = 0;
double getBalance() const { return balance; }
void deposit(double amount) { balance += amount; }
void withdraw(double amount) { balance -= amount; }
virtual std::string toString() const;
};
#endif // ACCOUNT_H
#include "Account.h"
#include "Customer.h"
std::string Account::toString() const {
return number + ":" + customer->toString() + ":" + std::to_string(balance);
}
#ifndef BANK_H
#define BANK_H
#include <set>
#include <string>
#include <memory>
#include "Account.h"
class Bank {
private:
// Unique pointers for Accounts. Ensures exclusive ownership of Account.
std::set<std::unique_ptr<Account>> accounts;
public:
void add(std::unique_ptr<Account> account) {
// std::move for transfer of pointer ownership.
accounts.insert(std::move(account));
}
void accrue(double rate) {
// keyword 'auto' automatically detects and assigns the type of a variable.
// When/How to use auto?
// 'auto&&': Universal reference. Used for modifying or moving elements.
// Works with both lvalues and rvalues.
// Is read-only with const containers.
// 'auto&': Lvalue reference. Used to directly modify elements in the container.
// 'auto const&': Constant lvalue reference. Used for read-only access.
// 'auto': Creates copies of elements
for (auto& account : accounts) { //auto& is used to modify each account in the accounts container without making unnecessary copies.
account->accrue(rate);
}
}
std::string toString() const;
};
#endif // BANK_H
#include "Bank.h"
std::string Bank::toString() const {
std::string r;
for (const auto& account : accounts) {
r += account->toString() + "\n";
}
return r;
}
#include <iostream>
#include <memory>
#include "Bank.h"
#include "Customer.h"
#include "CheckingAccount.h"
#include "SavingAccount.h"
int main() {
Bank bank;
// Shared Customer object. shared_ptr is a smart pointer that can share ownership with other shared_ptrs.
auto c = std::make_shared<Customer>("Ann");
// make_unique is used to create unique_ptr instances
bank.add(std::make_unique<CheckingAccount>("01001", c, 100.00));
bank.add(std::make_unique<SavingAccount>("01002", c, 200.00));
bank.accrue(0.02);
std::cout << bank.toString();
return 0;
}
public class Customer {
private String name;
public Customer(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
public class CheckingAccount extends Account {
public CheckingAccount(String number, Customer customer, double balance) {
this.number = number;
this.customer = customer;
this.balance = balance;
}
public void accrue(double rate) {}
}
public class SavingAccount extends Account {
private double interest = 0;
public SavingAccount(String number, Customer customer, double balance) {
this.number = number;
this.customer = customer;
this.balance = balance;
}
public void accrue(double rate) {
interest += balance * rate;
balance += balance * rate;
}
}
public abstract class Account {
protected String number;
protected Customer customer;
protected double balance;
public abstract void accrue(double rate);
public double balance() {
return balance;
}
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) {
balance -= amount;
}
public String toString() {
return number + ":" + customer + ":" + balance;
}
}
import java.util.*;
public class Bank {
private Set<Account> accounts = new HashSet<Account>();
public void add(Account account) {
accounts.add(account);
}
public void accrue(double rate) {
for (Account account : accounts)
account.accrue(rate);
}
public String toString() {
String r = "";
for (Account account : accounts)
r += account + "\n";
return r;
}
public static void main(String[] args) {
Bank bank = new Bank();
Customer c = new Customer("Ann");
bank.add(new CheckingAccount("01001", c, 100.00));
bank.add(new SavingAccount("01002", c, 200.00));
bank.accrue(0.02);
System.out.println(bank);
}
}
g++ src/*.cpp -I include -o BankingApp
./BankingApp
valgrind --leak-check=full ./BankingApp
01001:Ann:100.000000
01002:Ann:204.000000
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 11 allocs, 11 frees, 74,146 bytes allocated
All heap blocks were freed -- no leaks are possible
This example shows the C++ version of the C source code implementation of a Double Ended Queue.
#ifndef DEQ_HPP
#define DEQ_HPP
struct Data {
int value;
Data(int val) : value(val) {}
Data() : value(0) {}
};
enum End { Head, Tail, Ends };
class deq {
public:
//constructor/deconstructor
deq();
~deq();
//previously static functions
void put(End e, const Data& d);
Data* ith(End e, int i);
Data* get(End e);
Data* rem(End e, const Data& d);
//previous extern functions
void deq_head_put(const Data& d);
Data* deq_head_get();
Data* deq_head_ith(int i);
Data* deq_head_rem(const Data& d);
void deq_tail_put(const Data& d);
Data* deq_tail_get();
Data* deq_tail_ith(int i);
Data* deq_tail_rem(const Data& d);
//C++: const keyword indicates read-only ->memory protection
//C:varaibles that cannot be changed after intialization
void deq_str() const;
int deq_length() const {return len;}
private:
struct Node {
Node* np[Ends];
Data* data;
Node(Data* d) : data(d), np{nullptr, nullptr} {}
};
Node* ht[Ends];
int len;
void deq_delete();
};
#endif
#include <iostream>
#include <stdexcept>
#include "deq.hpp"
deq::deq() : ht{nullptr, nullptr}, len(0) {}
deq::~deq() {
deq_delete();
}
void deq::put(End e, const Data& d) {
Data* newData = new Data(d);
Node* curr = new Node(newData);
if (!ht[Head] && !ht[Tail]) {
ht[Head] = ht[Tail] = curr;
} else {
if (e == Head) {
curr->np[Tail] = ht[Head];
if (ht[Head]) ht[Head]->np[Head] = curr;
ht[Head] = curr;
} else { //Tail
curr->np[Head] = ht[Tail];
if (ht[Tail]) ht[Tail]->np[Tail] = curr;
ht[Tail] = curr;
}
}
len++;
}
Data* deq::ith(End e, int i) {
if (i < 0 || i >= len) throw std::out_of_range("Index Out of Bounds");
Node* temp = (e == Head) ? ht[Head] : ht[Tail];
for (int j = 0; j < i; ++j) {
temp = temp->np[e == Head ? Tail : Head];
}
return temp->data;
}
Data* deq::get(End e) {
if (!ht[e]) throw std::runtime_error("Null Pointer");
Node* node = ht[e];
Data* value = node->data;
ht[e] = node->np[e == Head ? Tail : Head];
if (ht[e]) {
ht[e]->np[e == Head ? Head : Tail] = nullptr;
} else {
ht[Tail] = ht[Head] = nullptr;
}
delete node;
len--;
return value;
}
Data* deq::rem(End e, const Data& d) {
Node* curr = ht[e];
Node* prev = nullptr;
while (curr) {
if (curr->data->value == d.value) {
if (prev) {
prev->np[e == Head ? Tail : Head] = curr->np[e == Head ? Tail : Head];
} else {
ht[e] = curr->np[e == Head ? Tail : Head];
}
if (ht[e]) {
ht[e]->np[e == Head ? Head : Tail] = prev;
} else {
ht[Tail] = ht[Head] = nullptr;
}
Data* value = curr->data;
delete curr;
len--;
return value;
}
prev = curr;
curr = curr->np[e == Head ? Tail : Head];
}
return nullptr;
}
void deq::deq_head_put(const Data& d) {
put(Head, d);
}
Data* deq::deq_head_get() {
return get(Head);
}
Data* deq::deq_head_ith(int i) {
return ith(Head, i);
}
Data* deq::deq_head_rem(const Data& d) {
return rem(Head, d);
}
void deq::deq_tail_put(const Data& d) {
put(Tail, d);
}
Data* deq::deq_tail_get() {
return get(Tail);
}
Data* deq::deq_tail_ith(int i) {
return ith(Tail, i);
}
Data* deq::deq_tail_rem(const Data& d) {
return rem(Tail, d);
}
void deq::deq_str() const {
const Node* current = ht[Head];
std::cout << "ORDER(H->T): ";
while (current != nullptr) {
std::cout << current->data->value << " ";
current = current->np[Tail];
}
std::cout << std::endl;
}
void deq::deq_delete() {
Node* current = ht[Head];
while (current != nullptr) {
Node* temp = current;
current = current->np[Tail];
delete temp->data;
delete temp;
}
ht[Head] = nullptr;
ht[Tail] = nullptr;
len = 0;
}
#include <iostream>
#include "deq.hpp"
int main() {
deq q;
//==============================TESTS================================================
std::cout << "" << std::endl;
std::cout << "====TEST 1: Put and Get functions for both Tail and Head====" << std::endl;
std::cout << "" << std::endl;
//deq_head_put()
//EXPECTED ORDER: 5 10 15 20
std::cout << "Head put: 20" << std::endl;
std::cout << "Head put: 15" << std::endl;
std::cout << "Head put: 10" << std::endl;
std::cout << "Head put: 5" << std::endl;
q.deq_head_put(Data(20));
q.deq_head_put(Data(15));
q.deq_head_put(Data(10));
q.deq_head_put(Data(5));
q.deq_str();
std::cout << "" << std::endl;
//deq_tail_put
//EXPECTED ORDER: 5 10 15 20 25 30 35 40
std::cout << "Tail put: 25" << std::endl;
std::cout << "Tail put: 30" << std::endl;
std::cout << "Tail put: 35" << std::endl;
std::cout << "Tail put: 40" << std::endl;
q.deq_tail_put(Data(25));
q.deq_tail_put(Data(30));
q.deq_tail_put(Data(35));
q.deq_tail_put(Data(40));
q.deq_str();
std::cout << "" << std::endl;
//deq_head_get()
//remove half with deq_head_get()
std::cout << "Head get: " << q.deq_head_get()->value << std::endl;
std::cout << "Head get: " << q.deq_head_get()->value << std::endl;
std::cout << "Head get: " << q.deq_head_get()->value << std::endl;
std::cout << "Head get: " << q.deq_head_get()->value << std::endl;
q.deq_str();
std::cout << "Length: " << q.deq_length() << std::endl;
std::cout << "" << std::endl;
//deq_tail_get()
std::cout << "Tail get: " << q.deq_tail_get()->value << std::endl;
std::cout << "Tail get: " << q.deq_tail_get()->value << std::endl;
std::cout << "Tail get: " << q.deq_tail_get()->value << std::endl;
std::cout << "Tail get: " << q.deq_tail_get()->value << std::endl;
q.deq_str();
std::cout << "Length: " << q.deq_length() << std::endl;
std::cout << "" << std::endl;
std::cout << "====TEST 2: Ith and Rem functions for both Tail and Head====" << std::endl;
std::cout << "" << std::endl;
//ORDER: 5 10 15 20 25 30 35 40
q.deq_head_put(Data(20));
q.deq_head_put(Data(15));
q.deq_head_put(Data(10));
q.deq_head_put(Data(5));
q.deq_tail_put(Data(25));
q.deq_tail_put(Data(30));
q.deq_tail_put(Data(35));
q.deq_tail_put(Data(40));
q.deq_str();
std::cout << "" << std::endl;
//deq_head_ith
std::cout << "Head Ith: " << q.deq_head_ith(0)->value << std::endl;
std::cout << "Head Ith: " << q.deq_head_ith(1)->value << std::endl;
std::cout << "Head Ith: " << q.deq_head_ith(2)->value << std::endl;
std::cout << "Head Ith: " << q.deq_head_ith(3)->value << std::endl;
std::cout << "Head Ith: " << q.deq_head_ith(4)->value << std::endl;
std::cout << "" << std::endl;
//deq_tail_ith
std::cout << "Tail Ith: " << q.deq_tail_ith(0)->value << std::endl;
std::cout << "Tail Ith: " << q.deq_tail_ith(1)->value << std::endl;
std::cout << "Tail Ith: " << q.deq_tail_ith(2)->value << std::endl;
std::cout << "Tail Ith: " << q.deq_tail_ith(3)->value << std::endl;
std::cout << "Tail Ith: " << q.deq_tail_ith(4)->value << std::endl;
std::cout << "" << std::endl;
//deq_head_rem
q.deq_str();
std::cout << "Head Rem: " << q.deq_head_rem(5)->value << std::endl;
std::cout << "Head Rem: " << q.deq_head_rem(10)->value << std::endl;
std::cout << "Head Rem: " << q.deq_head_rem(15)->value << std::endl;
std::cout << "Head Rem: " << q.deq_head_rem(20)->value << std::endl;
q.deq_str();
std::cout << "" << std::endl;
std::cout << "Tail Rem: " << q.deq_tail_rem(25)->value << std::endl;
std::cout << "Tail Rem: " << q.deq_tail_rem(30)->value << std::endl;
std::cout << "Tail Rem: " << q.deq_tail_rem(35)->value << std::endl;
std::cout << "Tail Rem: " << q.deq_tail_rem(40)->value << std::endl;
q.deq_str();
std::cout << "" << std::endl;
return 0;
}
#ifndef DEQ_H
#define DEQ_H
typedef void *Deq;
typedef void *Data;
extern Deq deq_new();
extern int deq_len(Deq q);
extern void deq_head_put(Deq q, Data d);
extern Data deq_head_get(Deq q);
extern Data deq_head_ith(Deq q, int i);
extern Data deq_head_rem(Deq q, Data d);
extern void deq_tail_put(Deq q, Data d);
extern Data deq_tail_get(Deq q);
extern Data deq_tail_ith(Deq q, int i);
extern Data deq_tail_rem(Deq q, Data d);
typedef char *Str;
typedef void (*DeqMapF)(Data d);
typedef Str (*DeqStrF)(Data d);
extern void deq_map(Deq q, DeqMapF f);
extern void deq_del(Deq q, DeqMapF f);
extern Str deq_str(Deq q, DeqStrF f);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "deq.h"
#include "error.h"
typedef enum {Head,Tail,Ends} End;
typedef struct Node {
struct Node *np[Ends];
Data data;
} *Node;
typedef struct {
Node ht[Ends];
int len;
} *Rep;
static Rep rep(Deq q) {
if (!q) ERROR("zero pointer");
return (Rep)q;
}
static void put(Rep r, End e, Data d) {
if((e==0)||(e==1)) {
Node curr = (Node)malloc(sizeof(*curr));
curr->data = d;
if((r->ht[0] == NULL) && (r->ht[1] == NULL)) {
curr->np[0] = NULL;
curr->np[1] = NULL;
r->ht[0] = curr;
r->ht[1] = curr;
} else {
if(e == 0) {
curr->np[0] = NULL;
curr->np[1] = r->ht[0];
r->ht[0]->np[0] = curr;
r->ht[0] = curr;
}
else if(e == 1) {
curr->np[1] = NULL;
curr->np[0] = r->ht[1];
r->ht[1]->np[1] = curr;
r->ht[1] = curr;
}
}
r->len++;
}
}
static Data ith(Rep r, End e, int i) {
if((e==0)||(e==1)) {
if((i >= (r->len)) || (i<0)) {
ERROR("Index Out of Bounds");
exit(EXIT_SUCCESS);
}
Node temp = NULL;
if(e==0) {
temp = r->ht[0];
if(i == 0) {
return temp->data;
}
for(int j=0; jnp[1];
}
} else {
temp = r->ht[1];
if(i == 0) {
return temp->data;
}
for(int j=0; jnp[0];
}
}
return temp->data;
} else {
ERROR("Incorrect End Specified");
exit(EXIT_SUCCESS);
}
}
static Data get(Rep r, End e) {
if (e == 0) {
if (r->ht[0] == NULL) {
ERROR("NULL HEAD POINTER");
exit(EXIT_SUCCESS);
}
Node head = r->ht[0];
Data value = head->data;
Node next = head->np[1];
if (next != NULL) {
next->np[0] = NULL;
}
free(head);
r->len--;
if (r->len == 0) {
r->ht[0] = NULL;
r->ht[1] = NULL;
} else {
r->ht[0] = next;
}
return value;
} else if (e == 1) {
if (r->ht[1] == NULL) {
ERROR("NULL TAIL POINTER");
exit(EXIT_SUCCESS);
}
Node tail = r->ht[1];
Data value = tail->data;
Node prev = tail->np[0];
if (prev != NULL) {
prev->np[1] = NULL;
}
free(tail);
r->len--;
if (r->len == 0) {
r->ht[0] = NULL;
r->ht[1] = NULL;
} else {
r->ht[1] = prev;
}
return value;
} else {
ERROR("Incorrect End Specified");
exit(EXIT_SUCCESS);
}
}
static Data rem(Rep r, End e, Data d) {
Data value = NULL;
if(e==0 || e==1) {
Node curr = r->ht[e];
while(curr != NULL) {
if(curr->data == d) {
value = curr->data;
if(curr->np[0] != NULL) {
curr->np[0]->np[1] = curr->np[1];
} else {
r->ht[0] = curr->np[1];
}
if (curr->np[1] != NULL) {
curr->np[1]->np[0] = curr->np[0];
} else {
r->ht[1] = curr->np[0];
}
free(curr);
r->len--;
return value;
}
if(e == 0) {
curr = curr->np[1];
} else {
curr = curr->np[0];
}
}
} else {
ERROR("Incorrect End Specified");
exit(EXIT_SUCCESS);
}
return value;
}
extern Deq deq_new() {
Rep r=(Rep)malloc(sizeof(*r));
if (!r) ERROR("malloc() failed");
r->ht[Head]=0;
r->ht[Tail]=0;
r->len=0;
return r;
}
extern int deq_len(Deq q) { return rep(q)->len; }
extern void deq_head_put(Deq q, Data d) { put(rep(q),Head,d); }
extern Data deq_head_get(Deq q) { return get(rep(q),Head); }
extern Data deq_head_ith(Deq q, int i) { return ith(rep(q),Head,i); }
extern Data deq_head_rem(Deq q, Data d) { return rem(rep(q),Head,d); }
extern void deq_tail_put(Deq q, Data d) { put(rep(q),Tail,d); }
extern Data deq_tail_get(Deq q) { return get(rep(q),Tail); }
extern Data deq_tail_ith(Deq q, int i) { return ith(rep(q),Tail,i); }
extern Data deq_tail_rem(Deq q, Data d) { return rem(rep(q),Tail,d); }
extern void deq_map(Deq q, DeqMapF f) {
for (Node n=rep(q)->ht[Head]; n; n=n->np[Tail])
f(n->data);
}
extern void deq_del(Deq q, DeqMapF f) {
if (f) deq_map(q,f);
Node curr=rep(q)->ht[Head];
while (curr) {
Node next=curr->np[Tail];
free(curr);
curr=next;
}
free(q);
}
extern Str deq_str(Deq q, DeqStrF f) {
char *s=strdup("");
for (Node n=rep(q)->ht[Head]; n; n=n->np[Tail]) {
char *d=f ? f(n->data) : n->data;
char *t; asprintf(&t,"%s%s%s",s,(*s ? " " : ""),d);
free(s); s=t;
if (f) free(d);
}
return s;
}
#include <stdio.h>
#include <stdlib.h>
#include "deq.h"
int main() {
//================================TESTS================================================
Deq q=deq_new();
//deq_head_put()
//EXPECTED ORDER: 1
printf("Head put: 1");
deq_head_put(q, "1");
char *a=deq_str(q,0);
printf("PRINT: %s\n",a);
//deq_tail_put()
//EXPECTED ORDER: 1 2 3 4
printf("Tail put: 2");
printf("Tail put: 3");
printf("Tail put: 4");
deq_tail_put(q, "2");
deq_tail_put(q, "3");
deq_tail_put(q, "4");
char *b=deq_str(q,0);
printf("PRINT: %s\n",b);
free(a);
free(b);
printf("\n");
//deq_head_rem()
//deq_tail_rem()
//EXPECTED OUTPUT:
printf("\n\nREM TEST\n");
deq_head_rem(q,"1");
deq_head_rem(q,"2");
deq_tail_rem(q,"3");
deq_tail_rem(q,"4");
char *c=deq_str(q,0);
printf("PRINT: %s\n",c);
free(c);
//deq_head_ith()
//deq_tail_ith()
deq_head_put(q, "4");
deq_head_put(q, "3");
deq_head_put(q, "2");
deq_head_put(q, "1");
printf("\n\nINDEX TEST\n");
//Check index value when starting from either side
int index1 = 3;
printf("index %d: value: %s\n", index1, deq_head_ith(q, index1)); //EXPECTED: 4
printf("index %d: value: %s\n", index1, deq_tail_ith(q, index1)); //EXPECTED: 1
int index2 = 0;
printf("index %d: value: %s\n", index2, deq_head_ith(q, index2)); //EXPECTED: 1
printf("index %d: value: %s\n", index2, deq_tail_ith(q, index2)); //EXPECTED: 4
//deq_head_get()
//deq_tail_get()
char *e=deq_str(q,0);
printf("PRINT: %s\n",e); //EXPECTED: PRINT: 1 2 3 4
free(e);
//check removes head
deq_head_get(q);
char *f=deq_str(q,0);
printf("PRINT: %s\n",f); //EXPECTED: PRINT: 2 3 4
free(f);
//check removes tail
deq_tail_get(q);
char *g=deq_str(q,0);
printf("PRINT: %s\n",g); //EXPECTED: PRINT: 2 3
free(g);
deq_head_rem(q,"2");
deq_head_rem(q,"3");
deq_del(q,0);
return 0;
}
#ifndef ERROR_H
#define ERROR_H
#include
#include
#define WARNLOC(file,line,kind,args...) do { \
fprintf(stderr,"%s:%d: ",file,line); \
fprintf(stderr,"%s: ",kind); \
fprintf(stderr,args); \
fprintf(stderr,"\n"); \
fflush(stderr); \
} while (0)
#define ERRORLOC(file,line,kind,args...) do { \
WARNLOC(file,line,kind,args); \
exit(1); \
} while (0)
#define WARN(args...) WARNLOC(__FILE__,__LINE__,"warning",args)
#define ERROR(args...) ERRORLOC(__FILE__,__LINE__,"error",args)
#endif
To compile and run the C++ program:
g++ -o myQ main.cpp deq.cpp
./myQ
====TEST 1: Put and Get functions for both Tail and Head====
Head put: 20
Head put: 15
Head put: 10
Head put: 5
ORDER(H->T): 5 10 15 20
Tail put: 25
Tail put: 30
Tail put: 35
Tail put: 40
ORDER(H->T): 5 10 15 20 25 30 35 40
Head get: 5
Head get: 10
Head get: 15
Head get: 20
ORDER(H->T): 25 30 35 40
Length: 4
Tail get: 40
Tail get: 35
Tail get: 30
Tail get: 25
ORDER(H->T):
Length: 0
====TEST 2: Ith and Rem functions for both Tail and Head====
ORDER(H->T): 5 10 15 20 25 30 35 40
Head Ith: 5
Head Ith: 10
Head Ith: 15
Head Ith: 20
Head Ith: 25
Tail Ith: 40
Tail Ith: 35
Tail Ith: 30
Tail Ith: 25
Tail Ith: 20
ORDER(H->T): 5 10 15 20 25 30 35 40
Head Rem: 5
Head Rem: 10
Head Rem: 15
Head Rem: 20
ORDER(H->T): 25 30 35 40
Tail Rem: 25
Tail Rem: 30
Tail Rem: 35
Tail Rem: 40
ORDER(H->T):