Taking the C++ hat off

I had been thinking that, coming from a C++ background, I was the only one sometimes feeling perplexed until I saw this post, which made me realize that I have to unlearn two important C++ ways of doing things: templates and polymorphism.

Does anyone have other suggestions to make the transition smoother?

5 Likes

Thank you, @vanvoorden.

I have been there before, but now I see that there are two new chapters. I am diving in. :slight_smile:

1 Like

I'd add, using destructors for any meaningful non-trivial action should be minimized in Swift due to destruction being a bit less deterministic compared to C++, plus the problem of error handling. This has been discussed on this forum (can't find the thread at the moment).

After a few years of coding in Swift I found myself using deinit mainly for two purposes: (1) debug printing, for cheap memory leak diagnostics and (2) manual memory deallocation in wrappers around some older API's.

Automatically deallocating external resources that can potentially fail is problematic in Swift. You can't always ignore a failure in your deinit: think of closing a file, socket, database connection, or some other network or peripheral transaction; these things require explicit finalization and I don't see how Swift can help with automating these things.

4 Likes

Swift is polymorphic the same way that C++ is: subclasses have a subtype relationship to their superclasses. In other words, an instance of a subclass can be used everywhere any of its superclasses is used.

Swift has both virtual and static dispatch, but doesn’t use the virtual keyword to distinguish the cases. In C++ terms:

  • Methods declared in a class’s main definition are implicitly virtual, and cannot be made non-virtual.
  • Methods declared in class extensions are not, and cannot be made, virtual.
  • When a subclass overrides a virtual method inherited from a superclass, Swift requires the override keyword. (Unless the method was originally defined in a protocol inherited by the superclass—see below.)
  • Override methods can be declared within a subclass’s main body or within an extension.
  • A subclass cannot declare a virtual method with the same name as a non-virtual method inherited from its superclass.
  • An extension of a subclass can declare a non-virtual method with the same name as a non-virtual method inherited from a superclass. It cannot use the override keyword.

Swift classes can inherit from at most one non-abstract base class. Protocols are similar to abstract base classes in C++, with a few notes:

  • Methods declared in the protocol’s main body are implicitly pure virtual, and cannot be made non-virtual.
  • Methods declared in the protocol’s main body can be made non-pure virtual by providing a “default” implementation of that method in an extension of that protocol.
  • Methods originally declared in protocol extensions are not, and cannot be made, virtual.
  • If a class directly inherits from a protocol, it must implement all methods declared in the protocol.
  • Implementations of methods inherited from a protocol do not use the override keyword, even if a superclass or the protocol itself has a (default) implementation of the method.
  • Protocols can inherit from other protocols, but they cannot inherit from classes.
  • An extension of a protocol can provide a default implementation for a method inherited from another protocol.
  • A method implementation in a protocol extension that does not provide a default implementation declares a new, non-virtual method on that protocol.
4 Likes

Thank you for the excellent summary, @ksluder.

To clarify, could you provide a concrete example?

protocol P { }
extension P {
  func foo() { print("P.foo()") }
}

protocol Q : P { } // Q inherits from P
extension Q {
  func foo() { print("Q.foo()") }
}

struct S : Q { }

(S() as P).foo() // prints "P.foo()"
(S() as Q).foo() // prints "Q.foo()"
1 Like