Stroustrup singled out these 2 constructs in C and said they represent nice, simplified models of memory usage.
Note a struct field can be a pointer.
Now I see that in C, there’s no other basic data structure. What about C++? Same! Java, c# seem to follow suit.
The c++ compiler is more flexible, more complex, more powerful, more engineered. Those other languages’ compilers lack those advanced features so they leverage the c++ compiler.
I would not say “most modern languages” rely on c++ for heavy-lifting.
I now recall that when I programmed in C, my code never used malloc() directly.
The library functions probably used malloc to some extent, but malloc was advanced feature. Alexandrescu confirmed my experience and said that c++ programmers usually make rather few malloc() calls, each time requesting a large chunk. Instead of malloc, I used mostly local variables and static variables. In contrast, C++ uses heap much more:
- STL containers are 99% heap-based
- virtual functions require pointer, and the target objects are usually on heap, as Alexandrescu said on P78
- pimpl idiom i.e. private implementation requires heap object, as Alexandrescu said on P78
- the c++ reference is used mostly for pass-by-reference. Pass-by-reference usually works with heap objects.
In contrast, C++ uses small chunks of heap memory.
Across languages, heap usage is is slow because
- In general OO programming uses more pointers more indirection and more heap objects
- heap allocation is much slower than stack allocation, as Stroustrup explained to me
- using a heap object, always always requires a runtime indirection. The heap object has no name, only an address !
- In Garbabe-Collected languages, there’s one more indirection.
https://en.cppreference.com/w/cpp/language/history briefly mentions
-  exception handling
-  templates
-  cast operators
-  dynamic_cast and typeid()
-  covariant return type
-  boost ref wrapper .. see std::reference_wrapper
-  GarbageCollector interface .. See c++ GC interface
-  std::next(), prev(), std::begin(), std::end() .. see favor std::begin(arrayOrContainer)
-  exception_ptr? not sure how useful
-  shared_lock — a RW lock
-  shared_timed_mutex .. see try_lock: since pthreads
-  std::exchange — comparable to std::swap() but doesn’t offer the atomicity of std::atomic_exchange()
- #1 Most important – modular jars featuring declarative module-descriptors i.e. requires and exports
- #2 linux cgroup support.. For one example, see Docker/java9 cpu isolation/affinity
- #3 G1 becoming default JGC.. CMS JGC: deprecated in java9
- REPL JShell
- private interface methods, either static or non-static
- Minor: C++11 style collection factory methods like
List<String> strings = List.of(“first”, “second”);
It’s unbelievable but not uncommon in Java history —
- Java9 release introduced significantly fewer and less impactful features than java8.
- Similarly, java5 overshadows java6 and java7 combined
java “override” rule permits covariant return — a overriding function to return a type D that’s subtype of B which is the original return type of the overridden method.
— ditto c++
https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Covariant_Return_Types. Most common usage is a “clone” method that
- returns ptr to Derived, in the Derived class
- returns ptr to Base in the Base class
Covariant return types work with multiple inheritance and with protected and private inheritance — these simply affect the access levels of the relevant functions.
I was wrong to say virtual mechanism requires exact match on return type.
CRT was added in c++98. ARM P211 (c 1990) explains why CRT was considered problematic in the Multiple Inheritance context.
- Bell labs Unix, in its early days, were able to run on hardware considered “underpowered” even by the standard of that day — P33 [[art of unix programming]]. I believe contemporary kernels were unable to run on those low-end machines.
- Linux (P77) has a similar characteristic. In the 1990’s the big commercial Unixes targeted enterprise-class hardware but Linux emphasized doing more with less. Today, Linux powers 99% of the world’s most powerful supercomputers but Linux also runs on low-end or obsolete hardware.
In both cases, I feel that an OS designed with very low minimum hardware requirement turned out to be actually more efficient, more adaptable, more versatile, more powerful than their conventional competitors.
Don’t spend too much time.. Based on my limited observations,
- As of 2007, the top dog was java.
- The dominance is even stronger in 2018.
- Q: how about in 10 years?
- A: I feel java will remain #1
Q: Why do I consider buy-side, sell-side and other financial tech shops as a whole and why don’t I include google finance?
A: … because there’s mobility between sub-domains within, and entry barrier from outside.
Buy-side tend to use more c++; Banks usually favor java; Exchanges tend to use … both c++ and java. The latency advantage of c++ isn’t that significant to a major exchange like Nsdq.
Let’s set the stage. A function returns a local Trade object “myTrade” by value. Will RVO kick in or move-semantic kicks in? Not both!
I had lots of confusions about these 2 features.[[effModernC++]] P176 has a long discussion and an advice — do not write std::move() hoping to “help” compiler on a local object being returned from a function
- If the local object is eligible for RVO then all compilers would elide the copy. Your std::move() would hinder the compiler and back fire
- if the local object is ineligible for RVO then compiler are required to return an rvalue object, often implicitly using st::move(), so your help is unneeded.
- Note local object returned by clone is a naturally-occurring temp object.
P23 [[c++stdLib]] gave 2-line answer:
- if Trade class has a suitable copy or move ctor, then compiler may choose to “elide the copy”. This was long implemented as RVO optimization in most compilers before c++11. https://github.com/tiger40490/repo1/blob/cpp1/cpp/rvr/moveOnlyType_pbvalue.cpp is my experiment.
- otherwise, if Trade class has a move ctor, the myTrade object is robbed
So if condition for RVO is present, then most likely your move-ctor will NOT run.
This is available in pthreads (pthread_mutex_trylock()).
I believe java and boost::thread are all based on that.
c++11 added std::timed_mutex which supports try-lock features
This is academic knowledge for the self-respected techie.
https://upload.wikimedia.org/wikipedia/commons/c/cd/Unix_timeline.en.svg and https://en.wikipedia.org/wiki/UNIX_System_V#/media/File:Unix_history-simple.svg show
- MacOS is based on BSD
- iOS and MacOS are based on Darwin
- linux contains no BSD or Unix codebase
- most commercial Unix versions are based on sysV
Recurring in stupid on-line MCQ interviews!
P185 ARM says a nested class NC can access static members of the enclosing class EC, and also local types of EC, all without fully qualified name.
However, NC can’t access EC’s non-static members. Java took a bold departure.
In c++11, a nested class is an implicit friend of the enclosing class. See https://stackoverflow.com/questions/5013717/are-inner-classes-in-c-automatically-friends
Q: how are java8 lambda expressions translated?
* Special helper method –
InvokeDynamic. You can see it in the bytecode
* Static methods – a non-capturing (stateless) lambda expression is simply converted to a static method
* a capturing lambda expression can also become a static method with the captures as additional method args. This may not be the actual compiler action, but it is a proven model. (Compare : separate chaining is one proven implementation of hash tables.)
However, static methods obscure an essential rule — the lambda expression’s type must “look like” a subtype of a SAM interface. Remember you often pass a lambda around as if it’s a SAM implementation instance.
So even if the actual work (like number crunching) is done in a static method, there must be some non-static wrapper method in a SAM subtype instance.
https://blog.codefx.org/java/dev/lambdas-java-peek-hood/ has some details on the evolution and design.
An interviewer once asked Q: with some of the c++11/14 features, there’s a concern that c++ is slowly becoming more and more like java and losing its advantage over java. What do you think?
A: I lack the deep insight and expertise and therefore not confident to answer this question, but personally I disagree 90%. Java is a very “clean” language compared to C# and c++ in many ways such as
- Every object is on heap. All custom types are constructed on heap.
- Every object is passed by reference; every primitive is passed by value.
- Non-static method are virtual by default.
- no pointer; no direct access to addresses
- much simpler templates, with type erasure
- higher-level, abstract concurrency constructs
- simpler multiple-inheritance
- strings are immutable
Java /restricts/ us to use a smaller set of higher-level tools, with a reduced level of control and restricted access to the lower level resources such as memory, sockets, kernel … Java is an automatic car, while c++ is a manual race car.
C++ offers a richer toolbox to the programmer. Messier, dirtier and more complex. Many of the power tools are fairly lowLevel. Beside auto_ptr, C++11 doesn’t remove from the toolbox. Therefore, you can use c++11 same way as c++03, OR you can use c++11 more like java.
Q: Is there a trend to use c++ more like java?
A: I notice growing adoption of references (instead of pointers), vector (instead of raw array), std::string (instead of c_str), smart pointer (instead of raw heap pointer) but they have nothing to do with c++11.
We can also examine the key features added to c++11. I think none of them is like java:
- move semantics — not like java at all
- smart pointers
- concurrency — completely different from java
- unordered containers
(see also post on “top 2 threading constructs in java ^ c#”)
[[Art of concurrency]] P89 has a 3-pager on pthreads vs win threads, the 2 dominant thread libraries. It claims “… most of the functionality in one model can be found in the other.”
In pthreads (like java), the 2 “most common” controls are mutex and condition var
In win threads library (and later dotnet), the 2 essential controls are
- events WaitHandles– kernel objects. Also known as event mutex, these are comparable to condVar, according to P175 [[object-oriented multithreading using c++]]
- mutex — kernel object, cross process
- CRITICAL_SECTION — userland object, single-process
https://stackoverflow.com/questions/8208546/in-java-5-0-statement-doesnt-fire-sigfpe-signal-on-my-linux-machine-why explains best.
http://stackoverflow.com/questions/6121623/catching-exception-divide-by-zero — c++ standard says division-by-zero results in undefined behavior (just like deleting Derived via a Base pointer without virtual dtor). Therefore programmer must assume the responsibility to prevent it.
A compliant c++ compiler could generate object code to throw an exception (nice:) or do something else (uh :-() like core dump.
If you are like me you wonder why no exception. Short answer — c++ is a low-level language. Stroustrup said, in “The Design and Evolution of C++” (Addison Wesley, 1994), “low-level events, such as arithmetic overflows and divide by zero, are assumed to be handled by a
dedicated lower-level mechanism rather than by exceptions. This enables C++ to match the behavior of other languages when it comes to arithmetic. It also avoids the problems that occur on heavily pipelined architectures where events such as divide by zero are asynchronous.”.
C doesn’t have exceptions and handles division-by-zero with some kind of run time error (http://en.wikibooks.org/wiki/C_Programming/Error_handling). C++ probably inherited that in spirit. However, [[c++primer]] shows you can create your own divideByZero subclass of a base Exception class.
java has no “undefined behavior” and generates an exception instead.
This is a blog post tying up a few discussions on this subject. It’s instructive to compare the different iterators in different contexts in the face of a tricky removal operation.
http://tech.puredanger.com/2009/02/02/java-concurrency-bugs-concurrentmodificationexception/ points out that ConcurrentModEx can occur even in single-threaded myList.remove(..). Note this is not using myIterator.remove(void).
[[Java generics]] also says single-threaded program can hit CMEx. The official javadoc https://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html agrees.
ConcurrentHashMap never throws this CMEx. See http://bigblog.tanbin.com/2011/09/concurrent-hash-map-iterator.html. Details? not available yet.
Many jdk5 concurrent collections have thread safe iterators. [[java generics]] covers them in some detail.
As seen in http://bigblog.tanbin.com/2011/09/removeinsert-while-iterating-stl.html, all STL graph containers (include slist) can cope with removals, but contiguous containers can get iterators invalidated. Java arrayList improves on it by allowing iterator to perform thread-safe remove. I guess this is possible because the iterator thread could simplify skip the dead node. Any other iterator is invalidated by CMEx. I guess the previous nodes can shift up.
- STL iterator invalidation results in undefined behavior. My test shows silent erroneous result. Your code continues to run but result can be subtly wrong.
- In java, before fail-fast, the outcome is also undefined behavior.
- Fail-fast iterator is the java solution to the iterator invalidation issue. Fail-fast iterators all throw CMEx, quickly and cleanly. I think CMEx is caused by structural changes — mostly removal and insertions.
- CHM came after fail-fast, and never throws CMEx