News for hackers. Stuff that matters.
Re:C++ by Example by a3w on Thursday September 22, 2022 @11:59AM (#32940801)
How do you fell about the pitfalls, like "if you inherit from a class, make sure the base destructor is virtual, or you just added a memory leak"

Some books on C++ teach you those, but I give up very early on learning languages with non-obvious expectations that offers few safeguards.

Re:C++ by Example by Chabsff on Thursday September 22, 2022 @12:04PM (#32940881)
That's not even particularly good advice, which just goes to show how subtle the pitfalls are. The rule really should be "If it is at all possible to delete instances from a pointer to the base class, then you need a virtual destructor". The overhead of adding a virtual destructor to a base class with no other virtual functions is far from negligible in many cases, so the distinction is meaningful (though modern guidance tends discourage non-polymorphic inheritance in the first place anyways).

The same thing goes for the good-old "Do not use raw pointers." The rule is actually: "Do not use raw pointers with implicit ownership semantics attached to them."

Re:C++ by Example by nyanpasu64 on Thursday September 22, 2022 @03:19PM (#32943437)
In retrospect I wish C++ used fat pointers rather than inheritance for virtual dispatch, like Rust but where interfaces/traits can have fields (Rust traits don't have fields and are less powerful than C++ base classes). This prevents accidentally overriding a base class method (eg. QWidget) by declaring a subclass method of the same name, eliminates the need to add a vtable pointer to object instances which you never cast to an interface pointer, and eliminates the need to juggle pointer offsets when performing multiple inheritance method calls, which can cause UB when performing C-style pointer casts and makes decompiling the code a nightmare (possibly fields in traits will add this requirement again, I'm not sure).
Re:C++ by Example by rramadass on Thursday September 22, 2022 @12:21PM (#32941112)
That is because C++ is not taught properly. It is not just a collection of syntactical features but a multi-paradigm language (i.e. supporting Procedural, Object-Oriented, Generic and Compile-time) where a feature is best suited to a particular paradigm and can also be used to express different concepts. Once you grasp this, things become quite clear and you understand how to design in C++.

To take your example; the same C++ "class" syntax can be used to express different concepts;

a) A user-defined value type eg. ComplexNumber. Therefore the dtor need not be virtual since the class is not designed for inheritance.

b) A pure interface used for "Interface Inheritance" i.e. all methods are "pure virtual". Therefore you need a pure virtual dtor. Java has the "Interface" keyword for this.

c) A class designed for a type hierarchy used for "Implementation Inheritance". Therefore you need a virtual dtor.

d) A class can also be used as a simple namespace eg. all static members; not designed for inheritance and thus no dtor needed.

Re:C++ by Example by bogomipz on Thursday September 22, 2022 @02:18PM (#32942655)
>"That is because C++ is not taught properly. It is not just a collection of syntactical features but a multi-paradigm language (i.e. supporting Procedural, Object-Oriented, Generic and Compile-time) where a feature is best suited to a particular paradigm and can also be used to express different concepts. Once you grasp this, things become quite clear and you understand how to design in C++."

Might you or someone else recommend a resource that does explain which features are suited for each the programming paradigms C++ support? Is there a modern resource that does teach it correctly like this? Knowing what features are relevant to your paradigm seems like a good way to approach learning C++.

Re:C++ by Example by rramadass on Saturday September 24, 2022 @07:42AM (#32961602)
C++ actually has two axes; 1) Paradigm axis (i.e. Procedural, Object-Oriented, Generic, Compile-time and Functional) and 2) Usage axis (i.e. Low-level bare-metal, Kernel, Library, Application). Every feature has an ideal application at the intersection of these two axes. Another way to look at it is how best to map concepts from the Problem-domain directly onto language features. Here techniques like Commonality/Variability analysis (bedrock of DDD, Patterns etc.) are used to express concepts using specific language features and then combine those into a coherent design.

Most of the above design ideas are found only in pre Modern C++ books. I don't know of any newer books which actually teach multi-paradigm design using Modern C++. So you will have to read the older books and then think about how you would map the same ideas into Modern C++. You might find the following useful;

Pre Modern C++:

1) Multi-Paradigm Design for C++ by James Coplien.

2) Scientific and Engineering C++: An Introduction With Advanced Techniques and Examples by John Barton & Lee Nackman.

3) Advanced C++ Programming Styles and Idioms by James Coplien.

4) Modern C++ Design: Generic Programming and Design Patterns Applied by Andrei Alexandrescu.

Modern C++:

1) Functional Programming in C++: How to improve your C++ programs using functional techniques by Ivan Cukic.

Re:C++ by Example by bogomipz on Saturday September 24, 2022 @10:36AM (#32962977)
Thank you for the wonderfully insightful and detailed response. This is really helpful. Cheers!
Re:C++ by Example by rramadass on Saturday September 24, 2022 @12:14PM (#32964035)
You are welcome.

For learning Modern C++, you might find these recommendations useful: https://news.ycombinator.com/item?id=32884432

Re:C++ by Example by kazinator on Thursday September 22, 2022 @01:51PM (#32942334)
Speaking of inheritance, how do you feel about this not requiring any diagnostics. It produces a weird result without containing any cast notation to subvert the type system.

  $ g++ -Wall -W -Wextra deriv.cc -o deriv
  $ ./deriv
  1.08654e-10

  $ cat deriv.cc
  #include <cstdio>

  struct Base {
  public:
    double y;
    Base() : y(1.0) {}
  };

  struct Derived : public Base  {
  public:
    int a, b;
    Derived() : a(0x3DDDDDDD), b(0x3DDDDDDD) {}
  };

  int main()  
  {
    Base *b = new Derived[5];
    b++;
    std::printf("%g\n", b->y);
  }
Re:C++ by Example by gpderetta on Friday September 23, 2022 @04:28AM (#32949342)
This is bad, but it is really no different than:

    int * b = new int;
    *++b;
Dereferencing past the end is always UB in C and C++ and it is hard to fix at the language level when using raw pointers.
Re:C++ by Example by jahnu on Thursday September 22, 2022 @02:16PM (#32942633)
For me this is the least of c++’s problems. Today I wouldn’t let that past a review as it should be using a std::array. If a c++ dev has a reason to drop down to this unsafe code they should know what the consequences are.
Re:C++ by Example by ncmncm on Thursday September 22, 2022 @01:04PM (#32941691)
Inheritance is a specialist technique, use of virtual members moreso. Most destructors in modern C++ are left to the compiler to generate.

It has been years since I last coded a virtual destructor.

Re:C++ by Example by tejohnso on Thursday September 22, 2022 @12:39PM (#32941352)
You'd want to read more than one book. And in my opinion you also need to digest the C++ Core guidelines and also use something like clang-tidy with high sensitivity, and when issues are flagged go dig into the details.

But then you realize it's just all too much, and you're looking for something less complex again.

I wonder how useful a C++ that I could understand in its entirety would be. But I don't understand english in its entirety either. I use what I know and get by just fine.