Learning C++ from Scratch
- Alessandro Salvato
- 6 giorni fa
- Tempo di lettura: 3 min
Let me start with something important.
I have no prior experience in C++.
I’m not coming from years of STL mastery.
I’m not refactoring legacy engines.
I’m not optimizing template metaprogramming.
I’m studying C++ from zero.
And this small exercise — modeling a financial instrument with Stock and Future — forced me to understand concepts that no high-level language lets you ignore.
This isn’t about finance.
It’s about structure, ownership, invariants, and disciplined abstraction.
The Abstract Base Class
Instead of starting directly with Stock or Future, I defined an abstract base class.
That decision alone changes how you think.
class FinancialInstrument
public:
explicit FinancialInstrument(const std::string& t)
: ticker_(t), n_(0), pmc_(0.0f), direction_(FLAT), multiplier_(1.0f)
{}
virtual ~FinancialInstrument() = default;
bool buy(unsigned int amount, float price);
bool sell(unsigned int amount, float price, float* gain);
bool sellshort(unsigned int amount, float price);
bool exitshort(unsigned int amount, float price, float* gain);
virtual float getMultiplier() const = 0;
virtual void info() const = 0;
protected:
float multiplier_;
private:
std::string ticker_;
unsigned int n_;
float pmc_;
direction_t direction_;
};Two things matter here:
The destructor is virtual.
getMultiplier() and info() are pure virtual.
This makes the class abstract.
You cannot instantiate it.
That is intentional.
It forces a separation between interface (contract) and implementation (behavior).
Stock: The Simplest Specialization
A stock has multiplier = 1.
Nothing fancy.
class Stock: public FinancialInstrument
{
public:
explicit Stock(const std::string& t)
: FinancialInstrument(t)
{
multiplier_ = 1.0f;
}
float getMultiplier() const override
{
return multiplier_;
}
void info() const override
{
std::cout << "[STOCK] multiplier=" << multiplier_ << "\n";
}
};What matters here is not complexity.
It’s the use of:
override
virtual dispatch
inherited interface
C++ forces you to declare intent explicitly.
Future: Parameterized Multiplier
Unlike stocks, futures contracts have a multiplier defined externally.
class Future : public FinancialInstrument
{
public:
Future(const std::string& t, float multiplier)
: FinancialInstrument(t)
{
multiplier_ = multiplier;
}
float getMultiplier() const override
{
return multiplier_;
}
void info() const override
{
std::cout << "[FUTURE] multiplier=" << multiplier_ << "\n";
}
};Now behavior diverges.
But the interface remains identical.
That is polymorphism done intentionally.
Extending the Model: Unrealized PnL
To make the class meaningful, I extended it with unrealized PnL:
float unrealizedPnL(float currentPrice) const
{
if(direction_ == LONG)
return (currentPrice - pmc_) * n_ * multiplier_;
if(direction_ == SHORT)
return (pmc_ - currentPrice) * n_ * multiplier_;
return 0.0f;
}What this snippet teaches:
State must be handled explicitly.
LONG and SHORT logic must be symmetric.
Multiplier matters.
Edge cases must be defined.
There is no runtime magic correcting mistakes.
You must encode every branch yourself.
Runtime Polymorphism in Action
Now the interesting part.
std::vector<std::unique_ptr<FinancialInstrument>> portfolio;
portfolio.push_back(std::make_unique<Stock>("AAPL"));
portfolio.push_back(std::make_unique<Future>("MES", 5.0f));
for(const auto& inst : portfolio)
{
inst->info();
}This works because:
The destructor is virtual.
The interface is pure virtual.
Ownership is encoded via std::unique_ptr.
Without virtual ~FinancialInstrument(), this design would be unsafe.
Without unique_ptr, ownership would be ambiguous.
C++ doesn’t let you pretend these details don’t matter.
What This Small Hierarchy Actually Teaches
Even this simple example forces you to confront:
Lifetime management
Ownership semantics
Interface vs implementation
Compile-time guarantees
Runtime dispatch
State invariants
When you are learning from scratch, you can’t rely on intuition.
You must reason everything out.
And that’s where the value lies.
A Subtle but Important Realization
Inheritance is not polymorphism.
Encapsulation is not secrecy.
Virtual is not design quality.
Templates are not “just faster”.
Every keyword in C++ encodes a structural choice.
And if you don’t understand the cost of that choice, the language will expose it.
Final Reflection
Studying C++ from zero is uncomfortable.
It removes abstraction cushions.
It forces you to think about:
who owns what
when things die
how state changes
where variability is resolved
how concurrency affects structure
But that discomfort is productive.
Because once you understand these mechanisms, you don’t just write C++ differently.
You design systems differently.
And that is exactly the kind of discipline that belongs in ^Algo.*$.



Commenti