Before I started to learn Go, every online opinion I would read about the language complained about the lack of generics and how OOP was dumbed down  and so on.

It made me put off learning it for quite some time, more than I would like to admit. Coming from a C++ background, OOP, generics and meta-programming was my daily bread.

It wasn’t until I had to actually learn Go that I saw what it offered me in terms of OOP and it was just enough. As such, I wanted to put a side-by-side comparison of typical C++ code that deals with classes, and it’s corresponding implementation in Go that does more or less the same thing.

This is by no means an exhaustive list of examples, but I thought it might prove useful for someone trying to figure out Go.

To run all Go examples, copy them to a file and run go run filename.go .

To run all C++  examples, copy them to a file and run g++ -o filename filename.cpp -std=c++14 && ./filename .

Class declaration

In C++:

#include <iostream>
#include <memory>
#include <string>

class MyClass {
    private:
        std::string property1;

        void Method1(std::string param1, int param2);

    public:
        std::string property2;

        MyClass(std::string constructor_argument);
        int Method2(int param);
};

MyClass::MyClass(std::string constructor_argument) {
    this->property1 = constructor_argument;
}

void MyClass::Method1(std::string param1, int param2) {
    std::cout << param1 << std::endl << param2 << std::endl;
    std::cout << this->property2 << std::endl;
}

int MyClass::Method2(int param) {
    this->Method1(this->property1, param);

    return param + 1;
}

int main(int argc, char *argv[]) {
    auto obj = std::make_unique<MyClass>("property 1 value");

    obj->property2 = "property 2 value";

    std::cout << obj->Method2(4) << std::endl;

    return 0;
}

Go equivalent:

package main

import "fmt"

type MyClass struct {
	// properties that start with a lowercase character are private
	property1 string
	// properties that start with an uppercase character are public
	Property2 string

	/*
		Keep in mind that public and private in Golang actually
		means exported by package. Code from the same package
		can access a structure's private properties and methods.
	*/
}

func NewMyClass(constructor_argument string) *MyClass {
	return &MyClass{property1: constructor_argument}
}

func (mc *MyClass) method1(param1 string, param2 int) {
	fmt.Printf("%s\n%d\n", param1, param2)
	fmt.Printf("%s\n", mc.property1)
}

func (mc *MyClass) Method2(param int) int {
	mc.method1(mc.property1, param)

	return param + 1
}

func main() {
	obj := NewMyClass("property 1 value")

	obj.Property2 = "property 2 value"

	fmt.Printf("%d\n", obj.Method2(4))

	// No return needed
}

Inheritance (sort of)

In C++:

#include <iostream>
#include <memory>
#include <string>

class BaseClass {
    public:
        std::string property1;
        void method1();
};

void BaseClass::method1() {
    std::cout << this->property1 << std::endl;
}

class DerivedClass : public BaseClass {
    public:
        std::string property2;
        void method2();
};

void DerivedClass::method2() {
    std::cout << this->property2 << std::endl;
}

int main(int argc, char *argv[]) {
    auto obj = std::make_unique<DerivedClass>();
    obj->property1 = "property 1 value";
    obj->property2 = "property 2 value";

    obj->method1();
    obj->method2();

    return 0;
}

Go equivalent:

package main

import "fmt"

type BaseClass struct {
	Property1 string

	// no need for method declaration here
}

func (bc *BaseClass) Method1() {
	fmt.Printf("%s\n", bc.Property1)
}

type DerivedClass struct {
	BaseClass // this is actually composition

	Property2 string
}

func (dc *DerivedClass) Method2() {
	fmt.Printf("%s\n", dc.Property2)
}

func main() {
	obj := &DerivedClass{}
	obj.Property1 = "property 1 value"
	obj.Property2 = "property 2 value"

	obj.Method1()
	obj.Method2()

	// no need to return
}

Interfaces

In C++:

#include <iostream>
#include <memory>
#include <string>

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

class Class1 : public MyInterface {
    public:
        void method() override;
};

void Class1::method() {
    std::cout << "Class 1" << std::endl;
}

class Class2 : public MyInterface {
    public:
        void method() override;
};

void Class2::method() {
    std::cout << "Class 2" << std::endl;
}

std::shared_ptr<MyInterface> NewClass1() {
    return std::make_shared<Class1>();
}

std::shared_ptr<MyInterface> NewClass2() {
    return std::make_shared<Class2>();
}

int main(int argc, char *argv[]) {
    auto obj1 = NewClass1();
    auto obj2 = NewClass2();

    obj1->method();
    obj2->method();

    return 0;
}

Go equivalent:

package main

import "fmt"

type MyInterface interface {
	Method()
}

type Class1 struct {
}

func (c1 *Class1) Method() {
	fmt.Println("Class 1")
}

type Class2 struct {
}

func (c2 *Class2) Method() {
	fmt.Println("Class 2")
}

func NewClass1() MyInterface {
	return &Class1{}
}

func NewClass2() MyInterface {
	return &Class2{}
}

func main() {
	obj1 := NewClass1()
	obj2 := NewClass2()

	obj1.Method()
	obj2.Method()
}

Conclusion

There are basic equivalences between traditional OOP languages like C++ and the syntax and functionality that Golang provides.

In it’s own simple way, Golang provides ways to implement encapsulation, inheritance and polymorphism. In my humble opinion, these mechanisms are enough for most object-oriented projects.