Initial Course
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
add_course_exercise(NAME classes_and_objects MODULE 06_oop_basics STANDARD 17
|
||||
SOURCES src/classes_and_objects.cpp test/classes_and_objects_test.cpp)
|
||||
add_course_exercise(NAME constructors MODULE 06_oop_basics STANDARD 17
|
||||
SOURCES src/constructors.cpp test/constructors_test.cpp)
|
||||
add_course_exercise(NAME inheritance MODULE 06_oop_basics STANDARD 17
|
||||
SOURCES src/inheritance.cpp test/inheritance_test.cpp)
|
||||
add_course_exercise(NAME polymorphism MODULE 06_oop_basics STANDARD 17
|
||||
SOURCES src/polymorphism.cpp src/inheritance.cpp test/polymorphism_test.cpp)
|
||||
@@ -0,0 +1,17 @@
|
||||
# Module 06: Object-Oriented Programming Basics
|
||||
|
||||
## Learning Goals
|
||||
|
||||
- Design classes with encapsulation
|
||||
- Implement constructors, destructors, and the Rule of Three/Five
|
||||
- Use inheritance and virtual functions
|
||||
- Apply polymorphism through base class interfaces
|
||||
|
||||
## Exercises
|
||||
|
||||
| Exercise | Topic |
|
||||
|----------|-------|
|
||||
| `classes_and_objects` | Members, methods, invariants |
|
||||
| `constructors` | RAII, special members |
|
||||
| `inheritance` | Base/derived classes |
|
||||
| `polymorphism` | Virtual functions, overriding |
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace oop {
|
||||
|
||||
class BankAccount {
|
||||
public:
|
||||
explicit BankAccount(std::string owner, double balance = 0.0);
|
||||
|
||||
[[nodiscard]] auto owner() const -> const std::string&;
|
||||
[[nodiscard]] auto balance() const -> double;
|
||||
|
||||
void deposit(double amount);
|
||||
auto withdraw(double amount) -> bool;
|
||||
|
||||
private:
|
||||
std::string owner_;
|
||||
double balance_;
|
||||
};
|
||||
|
||||
} // namespace oop
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace oop {
|
||||
|
||||
class StringBuilder {
|
||||
public:
|
||||
StringBuilder() = default;
|
||||
explicit StringBuilder(std::string initial);
|
||||
StringBuilder(const StringBuilder& other);
|
||||
auto operator=(const StringBuilder& other) -> StringBuilder&;
|
||||
~StringBuilder() = default;
|
||||
|
||||
void append(const std::string& text);
|
||||
[[nodiscard]] auto str() const -> std::string;
|
||||
[[nodiscard]] auto size() const -> std::size_t;
|
||||
|
||||
private:
|
||||
std::vector<char> buffer_;
|
||||
};
|
||||
|
||||
} // namespace oop
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace oop {
|
||||
|
||||
class Shape {
|
||||
public:
|
||||
virtual ~Shape() = default;
|
||||
[[nodiscard]] virtual auto name() const -> std::string = 0;
|
||||
[[nodiscard]] virtual auto area() const -> double = 0;
|
||||
};
|
||||
|
||||
class Circle : public Shape {
|
||||
public:
|
||||
explicit Circle(double radius);
|
||||
[[nodiscard]] auto name() const -> std::string override;
|
||||
[[nodiscard]] auto area() const -> double override;
|
||||
|
||||
private:
|
||||
double radius_;
|
||||
};
|
||||
|
||||
class Rectangle : public Shape {
|
||||
public:
|
||||
Rectangle(double width, double height);
|
||||
[[nodiscard]] auto name() const -> std::string override;
|
||||
[[nodiscard]] auto area() const -> double override;
|
||||
|
||||
private:
|
||||
double width_;
|
||||
double height_;
|
||||
};
|
||||
|
||||
} // namespace oop
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "inheritance.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace oop {
|
||||
|
||||
[[nodiscard]] auto total_area(const std::vector<std::unique_ptr<Shape>>& shapes) -> double;
|
||||
[[nodiscard]] auto shape_names(const std::vector<std::unique_ptr<Shape>>& shapes)
|
||||
-> std::vector<std::string>;
|
||||
|
||||
class Counter {
|
||||
public:
|
||||
Counter() = default;
|
||||
Counter(const Counter&) { ++copies_; }
|
||||
auto operator=(const Counter&) -> Counter& {
|
||||
++copies_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto copies() -> int;
|
||||
|
||||
private:
|
||||
inline static int copies_ = 0;
|
||||
};
|
||||
|
||||
} // namespace oop
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "classes_and_objects.hpp"
|
||||
|
||||
namespace oop {
|
||||
|
||||
BankAccount::BankAccount(std::string owner, double balance)
|
||||
: owner_(std::move(owner)), balance_(balance) {}
|
||||
|
||||
auto BankAccount::owner() const -> const std::string& { return owner_; }
|
||||
auto BankAccount::balance() const -> double { return balance_; }
|
||||
|
||||
void BankAccount::deposit(double amount) {
|
||||
// TODO: Add amount when positive
|
||||
(void)amount;
|
||||
}
|
||||
|
||||
auto BankAccount::withdraw(double amount) -> bool {
|
||||
// TODO: Return false if insufficient funds, else subtract and return true
|
||||
(void)amount;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace oop
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "constructors.hpp"
|
||||
|
||||
namespace oop {
|
||||
|
||||
StringBuilder::StringBuilder(std::string initial) {
|
||||
// TODO: Initialize buffer_ from initial
|
||||
(void)initial;
|
||||
}
|
||||
|
||||
StringBuilder::StringBuilder(const StringBuilder& other) : buffer_(other.buffer_) {}
|
||||
|
||||
auto StringBuilder::operator=(const StringBuilder& other) -> StringBuilder& {
|
||||
if (this != &other) {
|
||||
buffer_ = other.buffer_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void StringBuilder::append(const std::string& text) {
|
||||
// TODO: Append all characters from text
|
||||
(void)text;
|
||||
}
|
||||
|
||||
auto StringBuilder::str() const -> std::string {
|
||||
return std::string(buffer_.begin(), buffer_.end());
|
||||
}
|
||||
|
||||
auto StringBuilder::size() const -> std::size_t { return buffer_.size(); }
|
||||
|
||||
} // namespace oop
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "inheritance.hpp"
|
||||
|
||||
namespace oop {
|
||||
|
||||
Circle::Circle(double radius) : radius_(radius) {}
|
||||
auto Circle::name() const -> std::string { return "Circle"; }
|
||||
auto Circle::area() const -> double { return 3.141592653589793 * radius_ * radius_; }
|
||||
|
||||
Rectangle::Rectangle(double width, double height) : width_(width), height_(height) {}
|
||||
auto Rectangle::name() const -> std::string { return "Rectangle"; }
|
||||
auto Rectangle::area() const -> double { return width_ * height_; }
|
||||
|
||||
} // namespace oop
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "polymorphism.hpp"
|
||||
|
||||
namespace oop {
|
||||
|
||||
auto total_area(const std::vector<std::unique_ptr<Shape>>& shapes) -> double {
|
||||
double total = 0.0;
|
||||
for (const auto& shape : shapes) {
|
||||
// TODO: Add each shape's area using polymorphism
|
||||
(void)shape;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
auto shape_names(const std::vector<std::unique_ptr<Shape>>& shapes)
|
||||
-> std::vector<std::string> {
|
||||
std::vector<std::string> names;
|
||||
for (const auto& shape : shapes) {
|
||||
// TODO: Collect shape->name()
|
||||
(void)shape;
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
auto Counter::copies() -> int { return copies_; }
|
||||
|
||||
} // namespace oop
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "classes_and_objects.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(ClassesAndObjects, DepositAndWithdraw) {
|
||||
oop::BankAccount account("Ada", 100.0);
|
||||
account.deposit(50.0);
|
||||
EXPECT_DOUBLE_EQ(account.balance(), 150.0);
|
||||
EXPECT_TRUE(account.withdraw(30.0));
|
||||
EXPECT_DOUBLE_EQ(account.balance(), 120.0);
|
||||
EXPECT_FALSE(account.withdraw(1000.0));
|
||||
}
|
||||
|
||||
TEST(ClassesAndObjects, OwnerName) {
|
||||
oop::BankAccount account("Grace");
|
||||
EXPECT_EQ(account.owner(), "Grace");
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "constructors.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(Constructors, InitialAndAppend) {
|
||||
oop::StringBuilder builder("Hi");
|
||||
builder.append(" there");
|
||||
EXPECT_EQ(builder.str(), "Hi there");
|
||||
}
|
||||
|
||||
TEST(Constructors, CopySemantics) {
|
||||
oop::StringBuilder original("C++");
|
||||
oop::StringBuilder copy = original;
|
||||
copy.append("11");
|
||||
EXPECT_EQ(original.str(), "C++");
|
||||
EXPECT_EQ(copy.str(), "C++11");
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "inheritance.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(Inheritance, CircleArea) {
|
||||
oop::Circle circle(2.0);
|
||||
EXPECT_NEAR(circle.area(), 12.566370614359172, 1e-9);
|
||||
}
|
||||
|
||||
TEST(Inheritance, RectangleArea) {
|
||||
oop::Rectangle rect(3.0, 4.0);
|
||||
EXPECT_DOUBLE_EQ(rect.area(), 12.0);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "polymorphism.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(Polymorphism, TotalArea) {
|
||||
std::vector<std::unique_ptr<oop::Shape>> shapes;
|
||||
shapes.push_back(std::make_unique<oop::Rectangle>(2.0, 3.0));
|
||||
shapes.push_back(std::make_unique<oop::Circle>(1.0));
|
||||
EXPECT_NEAR(oop::total_area(shapes), 6.0 + 3.141592653589793, 1e-9);
|
||||
}
|
||||
|
||||
TEST(Polymorphism, ShapeNames) {
|
||||
std::vector<std::unique_ptr<oop::Shape>> shapes;
|
||||
shapes.push_back(std::make_unique<oop::Circle>(1.0));
|
||||
shapes.push_back(std::make_unique<oop::Rectangle>(1.0, 2.0));
|
||||
EXPECT_EQ(oop::shape_names(shapes), (std::vector<std::string>{"Circle", "Rectangle"}));
|
||||
}
|
||||
Reference in New Issue
Block a user