resource leak due to dtor bug

[[moreEffC++]] has a chapter dedicated to resource leaks in dtor. I have yet to read it, but here are my views:

A “Resource” means a heap object as a data member, in every case I know. In such a case, “Leak” means failure to call q[ delete ] on the data member.

To guarantee the q[ delete ], I feel one of the simplest yet most reliable strategies is a smart ptr as a data member. In particular

  • If a base-class subobject is already fully constructed when subclass ctor throws, the base-class dtor would run. Any resource in base-class is released if using smart ptr.
  • If a component subobject is already fully constructed when host ctor throws, the component dtor would run.
  • In the normal case of a fully constructed subclass or host object, then obviously its dtor would trigger the base-class dtor or component class dtor, in reverse order

However, replacing every ptr field with a smart ptr is costly and impractical.

op=() will call implicit cvctor if RHS is!!correct type

Below is an online c++ question, to predict the output (12310). The assignment “i=10” looks like a call to the synthesized assignment operator, but I overlooked the fact that RHS is a primitive int but LHS is Integer class, but assignment operator requires both sides to be Integer class type!

So an implicit conversion-ctor is required before the assignment operator is picked by the compiler.

A second knowledge gap — the synthesized assignment operator performs a bit-wise copy including the “num” field.

class Integer{
  int num;
public:
  Integer(){
    num=0;
    cout<<"1";
  }
  //explicit //would break the assignment
  Integer(int arg){
    cout<<"2";
    num=arg;
  }
  int getValue(){
    cout<<"3";
    return num;
  }
};
int main(){
  Integer i;
  i = 10;
  cout<<i.getValue();
}

subclass ctor/dtor using virtual func

See https://stackoverflow.com/questions/13440375/invoking-virtual-method-in-constructor-difference-between-java-and-c

Suppose both superclass and subclass defines a virtual cleanup() method.

— c++

… you lose the virtual i.e. “dynamic dispatch” feature. The subclass instance is not present so the only the base class implementation of cleanup() could run.

–java: let’s focus on ctor

… the subclass implementation of cleanup() would run, even though the subclass instance is not initialized — dangerous! See P70 [[elements of java style]]

deleted dtor

This is an obscure feature of c++11. As stated in my blogpost big4 can be synthesized as deleted #dtor, a class Acct can have its dtor deleted by the compiler !

So how can a program function with an object of this class?

First, note the only way to create that object is by operator-new. But compiler won’t allow us to do “delete myAcctPtr”. I think the only way compiler would even create an executable is by ensuring there’s no delete on Acct pointers.

I think the program will run until shutdown, so the Acct object on heap is never deleted.

shared_ptr {vector} for array@heap

You can’t create a raw ptr from array-new and then use it to create a shared_ptr. The final dtor of the shared_ptr club would call delete, not the required array-delete.

I would prefer shared_ptr<vector<int>>. See https://stackoverflow.com/questions/13061979/shared-ptr-to-an-array-should-it-be-used. The vector would be allocated on heap [1]. The ptr-to-vector would be deleted by the last “club member”. This deletion would trigger the (RAII) dtor of the vector. The RAII would clean up the memory of the underlying raw array.

[1] In contrast, when we instantiate a vector object as a local object the vector “shell” including the housekeeping fields are allocated on stack. See housekeeping^payload fields: vector,string,shared_ptr

If you must use shared_ptr<int> instead, then https://www.acodersjourney.com/top-10-dumb-mistakes-avoid-c-11-smart-pointers/ shows a simple custom deleter to invoke array-delete

c++11 throwing dtor: a few technicalities for IV

  • STL containers and std::swap() lose all exception guarantees when a payload class dtor throws
  • In a normal context (without double exception), if a dtor throws, it’s same as new() throwing exception. Stack unwinds as expected, and all subsequent dtors run as expected.
  • in c++11, all dtors are implicitly noexcept. If such a dtor throws, it triggers std::terminate() by default

Q: what if your dtor encounters a critical error but not allowed to throw exception?
A: https://www.kolpackov.net/projects/c++/eh/dtor-1.xhtml mentions a pre_destroy() member function that can throw. Dtor would call this function but catch and ignore the exception. Client code can also call this same function but handle the exception intelligently.

deleted^not-generated in c++11

[[effModernC++]] says that cp-ctor will not be generated in some cases, but in those cases is it necessary to put q(=delete) on cp-ctor?

A: I think q(=delete) has no effect on the binary. There’s simply no such ctor in the binary, with or without the q(=delete). The class has the cp-ctor deleted implicitly or explicitly.

A: q(=delete) is more explicit and improves readability. I think this is like adding q(virtual) to a derived class dtor when base dtor is already virtual

A: Without q(=delete), a new developer can add a cp-ctor.

See http://en.cppreference.com/w/cpp/language/default_constructor about no-arg ctor

pure virtual dtor

pure virtual dtor is a low-value obscure topic in 1) interview 2) zbs. So I won’t spend too much time on it.

https://stackoverflow.com/questions/1219607/why-do-we-need-a-pure-virtual-destructor-in-c addresses the rationale and justification for pure virtual dtor

https://www.geeksforgeeks.org/pure-virtual-destructor-c/ explains this dtor must be defined somewhere or compiler/linker would complain.

private dtor restricts instantiation to heap only

My test proves that declaration (even without definition) of a stack instance or global instance is illegal because compiler generates a (illegal) call to the private dtor!

Friend and static method can both also call the private dtor.

One usage of such a class — force all users to go trough factory to instantiate this class.

class PrivDtor{
  ~PrivDtor(){
        cout<<"dtor\n";
  }
public:
  static void destroy(PrivDtor* p){p->~PrivDtor();}
  static void del(PrivDtor* p){delete p;}
};

//PrivDtor globalInstance;  //won't compile
int main(){
  //PrivDtor stackInstance; //won't compile

  PrivDtor * p = new PrivDtor;
  //PrivDtor::del(p); // works!
  PrivDtor::destroy(p);  //works!
}

 

c++big4: prefer synthesized

I think it’s the author of [[safe c++]] who pointed out that if we have to maintain non-default big4, then it’s extra workload for the maintenance programmer. He argued convincingly that it’s not a good idea to require other programmers or yourself to “always remember to do something”

pointer as field –#1 pattern in c++ explains that shared_ptr as a pointer field allows us to use the default big4.

field destruction]class dtor and leaks: string,vector,shared_ptr

https://stackoverflow.com/questions/12068950/c-destructors-with-vectors-pointers has some scenarios that are realistic and practical.

1) if you have a local std::string instance behind a variable mystr, then dtor of mystr will clean up the heap memory that was allocated by the mystr ctor. This is one of the most basic usages of std::string. There’s No heap memory leak in this simple scenario.

1b) Ditto for a std::vector.

2) If you have a vector of raw pointer, then be careful. If you used new() for each raw ptr, then you need to call delete() otherwise memory would leak.

2b) shared_ptr is a great replacement. No memory leak even if you don’t call delete()

3) if your class C has a field as a raw ptr to A, then the C dtor will run the default dtor of the ptr. As Scott Meyers said, that default dtor is a no-op. Therefore by default memory leaks. You need to manually delete()

3b) smart-ptr-to-A as a field is a clean, recommended solution.

c++11 class removing the 4 copy operations

There are at least four main copy operations
a) copy-ctor
a2) move-ctor
b) operator=
b2) move-operator=

Each one can be declared “=delete”. In http://talesofcpp.fusionfenix.com/post-24/episode-eleven-to-kill-a-move-constructor, the author says you can have four combinations:
1) all_enabled
2) movable_only
3) copyable_only — rare. A meaningless design, according to the author.
4) all_deleted — most restricted, most locked-down

Some important library classes are movable_only, such as …. unique_ptr, and some thread objects

ref-counted copy-on-write string #MIAX exchange IV

I completely failed this 2011 IV question from MIAX options exchange:

Q: outline a ref-counted copy-on-write string class, showing all the function declarations
A: here’s my 2017 answer

struct Payload{ //this object must live outside any Str instance. If 1st Str instance in the group is destroyed, this object must live on.
	char * const arr;
	size_t const length;
	mutable size_t refCount;
	Payload(std::string const &);
};
class Str{
	Payload const * payload;
public:
	~Str(); //decrement refCount and if needed, delete the payload
	//Str(); //default ctor is useless since after construction we can't really modify this instance, due to copy-on-write
	Str(std::string const &);
	Str(Str const &); // similar to shared_ptr

	Str & Operator=(Str const & other) const; // will return a reference to a new instance constructed on heap (never on stack!). The new instance will have a ref-count initialized to 1
	Str & replace_with(std::string const &) const; //ditto

// optional utilities
	char const * c_str()    const; // <-- Hey mine is very similar to std::string
	Str(char const * arr, size_t const len); // <-- Hey mine looks similar to std::string
	friend ostream & operator<<(ostream &, Str const &);
};

big4 of a payload object in a std::vector

There are various claims on what specific big4 requirements a vector would impose on the payload objects.

Q: can no-arg ctor be private?
%%A: I doubt it. I remember [[essential c++]] or another well known author said when you create a vector or array of N instances, those instance are default-constructed.

Q: Can copy ctor be deleted or private?
AA: No. Reallocation requires copy ctor

Q: can dtor be private?
%%A: No. At reallocation time, old instances must be destructed.

[[optimized c++]] introduced the unofficial, unenforceable "framework" of value object vs entity object. Vector obviously hold value objects, so by right they should be copyable, with no shared ownership.

c++ ctor calls(confusing), another summary

See more details in my post https://bintanvictor.wordpress.com/2013/08/12/c-no-arg-ctor-call-without-new-confusing/. Is there anything new
here? No, just another summary.

On heap (“new”) or with ctor args, things are more clear-cut and we make fewer mistakes. My confusions are the no-arg ctor calls on
the stack.

Animal a1; //ctor. Standard idiom but I would try to use the alternative form below.
Animal a2 = Animal(); // ctor – temp obj. This form is possibly less efficient, but this form is flexible —
Animal a3 = Animal(someArt)

// Above syntax may or may not create additional temp objects, in c++03 or c++11.

Animal aX(); // NOT ctor
throw Animal(); //ctor – temp obj
function6(Animal() );//ctor – temp obj

Rule of thumb — All the Animal() expressions create a temp.

new-expression alloates AND invokes ctor…most of the time

Neither the allocation or the ctor step is universal and guaranteed to happen.

1) ctor

If allocation fails then no “raw memory” is there to initialize.

new int; // This won’t invoke any ctor since “int” is not a class/struct and doesn’t have a ctor.

2) allocation

It’s possible to bypass the allocation, if you use placement-new

op-new : no DCBC rule

B’s op-new is bypassed by D’s op-new [1]
B’s ctor is always used (never bypassed) by D’s ctor.

This is a interesting difference.

Similarly, an umbrella class’s op-new [1] would not call a member object’s op-new. See [[more effC++]]

These issues are real concerns if you want to use op-new to prohibit heap instantiation of your class.

See http://bigblog.tanbin.com/2012/01/dcbc-dtor-execution-order.html

[1] provided these two classes each define an op-new()

By the way, op-new is a static member operator, but is still inherited.

mv-semantic | use cases rather few

I think the use case for mv-constructs is tricky. In many simple contexts mv-constructs actually don’t work.

Justification for introducing mv-semantic is clearest in one scenario — a short-lived but complex stack object is passed by value into a function. The argument object is a temp copy — unnecessary.

Note the data type should be a complex type like containers (including string), not an int. In fact, as explained in the post on “keywords”, there’s usually a pointer field and allocation.

Other use cases are slightly more complex, and the justification is weaker.

Q: [[c++standard library]] P21 says ANY nontrivial class should provide a mv ctor AND a mv-assignment. Why? (We assume there’s pointer field and allocation involved if no mv-semantics.)
%%A: To avoid making temp copies when inserting into container. I think vector relocation also benefits from mv-ctor

[[c++forTheImpatient]] P640 shows that sorting a vector of strings can benefit from mv-semantic. Swapping 2 elements in the vector requires a pointer swap rather than a copying strings

non-virtual dtor: some random scenarios tested #IV

Background — How important are these scenarios? First off, tech quizzes are extremely important since you are judged just over a few questions. Second, these scenarios pop up by accidents, rather than be design, all the time in real projects. You better learn to deal with a drunken driver while on the road.

Q1: what if Base and Derived dtor both non-virtual and an autoVar is destroyed?
AA: Tested — ~Derived() and then ~Base().  See post on DCBC.

Q2: What if Base dtor is non-virtual but Derived is virtual, and a Derived auto variable is destroyed on the stack?
AA: Same as Q1. For an autoVariable that’s not deleted via a ptr, Derived ctor (virtual or not) runs, followed by Base dtor. Same DCBC

Q3: Base dtor is non-virtual but Derived is virtual. Derived heap pointer is assigned to a B pointer and deleted?
AA: only ~Base runs , in my g++ test, though officially UB.

Q4: Base and Derived are virtual. Derived heap pointer is assigned to a B pointer and deleted?
AA: ~Derived() then ~Base(). DCBC + dynamic dispatch

Note the well-known __undefinedBehavior__ affects delete only, not stack variables or static variables.Note virtual keyword affects pointer variable. Non-ref variables aren’t affected.

The object to destruct is a Derived
~B ~D st? delete heap D via B* or D* test result notes
1 nv nv stack ~D~B DCBC + static dispatch
2 nv virtual stack ~D~B DCBC + static dispatch
3 nv virtual B*  ~B only static dispatch. “virtual” ignored
4 v virtual D*  ~D~B DCBC + dynamic dispatch
5 v implicitly v D*  ditto ditto
struct B{
  ~B(){ cout<<"~B\n";}
};
struct D: public B{
  virtual ~D(){ cout<<"~D\n";}
};
int main(){
  B* myD=new D();
  delete myD;
}

dtor is simple thing.. really@@

update: [[safe c++]] has a concise argument that if ctor is allowed to throw exception, then it’s possible to have dtor skipped accidentally. Only an empty dtor can be safely skipped.

This is typical of c++ — destructor (dtor) is one of the most important features but quite tricky beneath the surface

* exception
* dtor sequence – DCBC
* virtual dtor
* synthesized dtor is usually no good if there’s any pointer field
* lots of undefined behaviors
* there are guidelines for dtor in a base class vs leaf class — never mindless
* objects put into containers need a reasonable dtor
* when the best practice is to leave the dtor to the compiler, you could look stupid by writing one, esp. in an interview.

* smart pointer classes is all about dtor

* RAII is all about dtor

* interplay with delete

*** placement new, array-delete vs delete

*** override operator delete

*** double-delete

*** ownership !

c++ bad_alloc handling — briefly

Q: What can you do when you get an std::bad_alloc exception?
A: Scott Meyers suggested overloading operator new and new-handler. I guess in financial apps it’s rarely needed. But we should really read the [[safe c++]] book written by a hedge fund coder.

A: I used to keep 60MB files in my C: drive, to be deleted when I run out of disk — same idea expressed in Item 7 in [[effC++]]

A: if it’s stack, it’s often due to deep recursion. You can set stack size —

gcc -Wl,–stack=xxxxx -Wl,–heap=yyyyy …

c++ no-arg ctor Call +! q[new] #confusing

(Any QQ value? Yes some crazy interviewers may ask.)

With “new” or with ctor arguments, the confusion is lower. Note since no “new” in this entire article, all the instances are constructed on stack or global area.

Compare to java, the c++ default construction syntax looks confusing even inconsistent —

   Account* addr = &Account() //instantiates temp.. and saves its address. Not sure if this is meaningful.

Note  Account() is parsed differently by compiler, depending on context. In each case below, Account() looks like a function call returning an Account on stack–
   Account() ; // on a line by itself this expression simply creates a local temp instance on stack then …DISCARDED.
 
Account().print(); // instantiates temp, then invokes a method

Account myAcct = Account(); //(tested) unpopular but rather consistent with new(), and consistent with other languages. Arguably Recommended to beginners though inefficient. http://stackoverflow.com/questions/11480545/explicit-copy-constructor explains what it does – 

{create a temporary Account on the rhs; Implicitly call the copy constructor to copy from that temporary into myAcct.Marking the copy ctor “explicit” would break this code, which is recommended by many authors.

    ifstream in = ifstream(“aaa.txt”); //broken – due to deleted copy ctor
    ifstream in(“aaa.txt”); //tested. P96[[essentialC++]]

    Account arr[] = {Account(),Account()}; //2 ctor calls. Actually, You can even pass args to initialize the two array elements.

   func1( Account() ); //creates a temp and pass it to func1(). Trailing parens are necessary. I feel this is practical and common, but I seldom notice it.
   throw Account(); //creates a temp and.. See P1035 [[c++Primer]]
   count_if(itr1 , itr2 , MyPredicateFunctor()); //creates a temp … See P107 [[DanielDuffy]] + P86[[essentialC++]]
———

std::string const var; // no parentheses … allocates a const instance
class MyClass{/*empty class*/};
MyClass var3; // ok allocates the object behind the variable without initializing it
MyClass const var2; // can’t compile but why?
Answer — You need to add empty no-arg ctor into MyClass to avoid a POD class.
————- 
(See the code below.) I personally prefer the Account(…) syntax above, and avoid #5/#6 below, but #5/#6 are popular. We just have to memorize the inconsistent syntax about them. #6 is clear and unambiguous. #9 seems to follow the #6 syntax “pattern” but alas! The correct way to achieve the purpose is #5.

    Account myAcct; // #5 declares a variable and allocates an Account object. Popular idiom, but somewhat inconsistent with other syntax constructs like the (123) caseTrailing parens are problematic and will lead to #9:
    Account myAcct(); // #9 NOT a ctor call; declares a function! Anything parseable as a func declaration is parsed that way. See P34 [[effSTL]]

     Account myAcct(123); // #6 initialize the myAcct variable with a stack object,  not  discarded.


y destructors should never throw #le2LinYu

Hi Lin Yu,

You once asked me exactly why destructors should not throw. I just read about it. Compiler would turn a blind eye if your destructor calls some function1 (that in turn calls some function2, that in turn calls some function3 …) that could throw an exception. Over 5 years in production the destructor may not throw, but what happens when it does throw? Maybe nothing bad happens, but maybe one of these things happen

– if the destructor is invoked as part of stack unwinding, then system would probably terminate
– If your object’s destructor has the hidden (destructive) power to throw exceptions, then the STL containers won’t store this object. If you still put this object into an STL container, then I don’t know what happens. Perhaps undefined behavior. If your class is to be used with the STL, then the STL explicitly forbids exception throwing from your class destructor.
– your class can’t be used as member or base-class of another class

Even though the consequence is not documented, the language standard DOES specify when and where destructors should NOT throw. Looks like you might (in theory) get away with an exception thrown from a destructor, but many parts of c++ language do not allow it. In reality, the consequence is probably harmful rather than harmless.

I hope this letter clarifies the issue rather than add to your confusion. Feel free to reply.

Tan Bin

select2perform/brain-bench c++ Q&A

Q: vector1.erase(remove(vector1.rbegin(),vector.rend(),someValue).base())

Q: To put 5 identical values into a vector,
std::fill_n() ?
std::fill() ?
Q: If class E virtually extends C and extends D, where C/D both virtually extend B, what’s the construction sequence?
AA: P1000 [[primer]]

Q: find intersection of 2 compatible vectors?
%%A: std::find_first_of()
Q: can you pass a float into a func(int) or func(char)?
AA: yes but if you cout in func(), it interprets and prints the object differently.
Q: can a non-static reference field be initialized upon declaration?
%%A: i don’t think so, reference field or nonref. The java-style quick initi is illegal for non-static.
AA: gcc allows java-style init only for a static AND const field.

Q: can you “extern” a prototype and then declare the same prototype again?
AA: legal

Q[u]:difference between “new Account” and “new Account()” with the parentheses?
A: subtle difference. See other posts on this blog such as http://bigblog.tanbin.com/2012/07/default-initializevalue-initialize-new.html

Q[u]: declare a pointer to a method accepting an int and returning void. Need to memorize the exact syntax
AA: void (Dog::*ptr2method) (int);

Q: my ex spec mentions std::runtime_error. Can I throw a range_error, or an std::exception, or std::invalid_argument?
AA:

 exception
runtime_error
range_error
exception
logic_error
invalid_argument

Q: reverse print a string using copy to cout
AA: copy(str.rbegin(), str.rend(), ostream_iterator< char >(cout, ” “));
* The ” ” 2nd argument is not necessary, but I’d suggest we memorize just a single form of the ostream_iterator ctor, the 2-arg form.
* The type is char not  ptr-to-char

Q: using N1::f; f(); using N2::f; f();
AA: GCC complains 2nd all to f() is ambiguous. http://www.cplusplus.com/doc/tutorial/namespaces/ explains 

Q[u]: multiple inheritance dreaded diamond – what if one of the 2 “virtual” keywords is omitted? How many times will you call the base constructor?
AA: http://bigblog.tanbin.com/2010/07/virtual-inheritance-diamond-malformed.html

Q[u]: anything wrong with
Base & var = static_cast(*(new Derived));
AA: legal in gcc.

[u = unimportant/trivial/unlikely to be issue in practice. Often compiler will inform you clearly.]

throwing dtor: %%justified use cases

Status — still I don’t have a well-justified use case.

I feel throwing dtor is frowned upon but not “illegal”. In practice it’s seldom done by design. When used, it’s not worth the analysis. In interviews, however, this receives disproportionate attention.

However, in practice, there are reasons to break the rule. Suppose I have

int i1, i2;// temp variables,
try{
  i1 = myobj->eat();
  myobj->drink(&i1);
  //....
  delete myobj;
}catch(business_exception & be){
  //handle exception using the temp variables i1, i2 etc
}

Since eat(), drink() etc and dtor all throw the same business_exception, this code is clean and maintainable. If we need to throw more than one exception type from those 3 functions, we can easily add the code. The same exception handler is used as a catch-all.

It would be messy to pass the temp variables i1, i2 etc into myobj dtor and replicate the same exception-handling logic therein.

So in this case, I’d make myobj dtor throw business_exception.

Now, as described in [[moreEffC++]] myobj dtor is invoked as part of stack unwinding due to another exception? [1] Well, in this case, I know that’s a fatal scenario and I do want system to crash anyway, like an assertion error, so the terminate() behavior is not unacceptable.

In other words, myobj’s class is written such that its dtor should throw exception only under normal object destruction and should never be part of an exceptional stack unwinding. In such a case, no one should misuse this class in an exception-unsafe context. If they ignore the restrictions on this class, they could get this dtor invoked as part of an exceptional stack unwinding, and the consequence is something they must deal with.

[1] in c++11, system will trigger std::terminate() whether or not this is part of unwinding. See https://akrzemi1.wordpress.com/2011/09/21/destructors-that-throw/

DCBC — dtor execution order

DDDerived class dtor, which should not explicitly invoke C or B.
CCComponent (ie field) dtor
BBBase dtor
CCComponent of BBBase class

Compiler arranges these steps to execute in a single thread. See P277 ARM

A1: same DCBC according to EffC++. I probably tested it.

A3: See P60 [[safe c++]]. The exception triggers stack unwinding — C B C. Exact same sequence except the very first step i.e. the D is skipped. Derived dtor is skipped because Derived ctor didn’t complete.

The simple rule in this “exceptional” scenario is “whenever a ctor completes without throwing, its dtor would be executed.

A2: constructed First, so destructed Last. Remember dtor^ctor is ALWAYS reverse order

Quizzes:
Q1: but what if i invoke Derived virtual dtor through “delete”?
Q2: Virtual base?
Q3: what if the derived ctor throws exception after B and C completed? Which dtors will/won’t run and in what order?

(To aid blog search, D-C-B-C)

c++ private ctor – purpose, subverting …

Q1: what are the consequences of making a class’s ctors all private?
A1a: can’t subclass.
A1b: can’t Embed in another class as a nonref field.
A1c: Outsiders can’t instantiate stack instances, and call qq(new MyClass) either — tested in my IDE. There lies a difference with private dtor —

$ A private Destructor prevents instantiation of ALL nonref like qq(Myclass myObj), either as a stack instance or as a global variable !!
$ A private Destructor prevents explicit delete of MyClass like qq(delete myPtr) but
$ A private Destructor doesn’t prevent qq(new MyClass).

See also P146 [[more eff c++]]

Now back to private ctor.
Q1b2: but how about java-style has-a? Note in java, all reference type fields live in the host object as a 32-bit pointer whereas the pointee lives outside the real estate of host object. C# struct fields live right on THAT real estate. C++ nonref fields are like c# struct.
%%A: I think a private MyClass ctor won’t affect host object instantiation because host object can use a null ptr field. Later on that ptr can reseat to a MyClass instance returned from some function.

Q: how about protected ctor?
%%A: can subclass. I think this is used mostly for virtual inheritance.

Q: what are the alternatives?
%%A: make Destructor private. See also P146 [[more eff c++]]

Q: justification?
%%A: there’s just one dtor to manage, but any number of ctor’s.

Q: if it’s not water tight, then how can it be subverted?
%%A: friends

virtual base-class initialization during ctor/dtor

Rule 2 — http://bigblog.tanbin.com/2012/01/dcbc-dtor-execution-order.html shows the dtor sequence. Ctor is the opposite — B-C-D. Let’s call it Rule 2

The exception to Rule 2 is virtual base — Virtual base is constructed before non-virtual bases.

However, this rule still holds —
Rule 1 — dtor / ctor are Always in reverse orders.

Therefore, virtual base is destroyed Last. See http://msdn.microsoft.com/en-us/library/8ff62s5k.aspx

P292 ARM has a concise and complete treatment.

prevent heap instantiation4my class #part2

[[C++FAQ]] and [[More Eff C++]] both cover related topics.

I feel it’s very useful to have classes that can only be instantiated on stack, because stack-variables are automatically reclaimed.

With these classes, no double-delete, no dangling pointer, and most importantly, no memory leak. Remember memory leak is the most hidden — hardest to identify.

::operator new() customization — ARM

ARM Jargon warning —
“the new-operator will call the function operator-new()” actually means
The new expression will call the function operator-new()

P60 ARM makes it clear that function operator-new can be overloaded — new-expression is not something you overload. Ultimately, op-new is really just a function and as such can be overloaded. The global qq{::operator new()} can be overloaded for class T, using T::operator new() [2]. Quite common and safe. See Scott Meyers and [[c++primer]]

Q: But can you overload the global  ::operator new()  ?
A: yes. P60 ARM Bullet point 2
A: yes. “Unlike class-specific new, global new is usually overloaded for debugging purposes. In some cases, however, you might want to overload global new and delete permanently, because you have a better allocation strategy or because you want more control over it.” according to [[C++InAction]] http://www.relisoft.com/book/tech/9new.html

[[c++TimesavingTechniques]] P128 has a rather simple yet practical replacement for global  ::operator new()

[2] the static method syntax. Class-specific Op-new() is implicitly static since you never call op-new on an existing instance to grab memory for a new instance.

initializer in copier — bypass no-arg ctor

Say your copy ctor is invoked (often implicitly;-), and you need to clone a field of type T. If you don’t use the field initializer syntax, then the copier first calls T’s no-arg ctor to “make room” for the field, then calls T’s operator= to modify its state.

If you choose the initializer syntax, then T’s no-arg ctor is not needed. T’s copier is used instead. Chain reaction — Host copier invoking field copier

#include
using namespace std;
template class Safe{
public:
    Safe(const T& obj){ // T aObj will instantiate
        cout<<"Safe copier called"<<endl;
        cout<<"before assignment, "<<&aObj<<endl;
        aObj = obj;
        cout<<"after assignment, "<<&aObj<<endl;
    }
//    Safe(const T& obj):aObj(obj){} // T aObj won’t instantiate
    ~Safe(){    }
    T aObj;
};

struct Date{
    Date(){
        cout<<"Date no-arg called with this = "<<this<<endl;
    }
    Date(int y, int m, int d){}
    Date(const Date& rhs){
        cout<<"Date copier called with this = "<<this<<endl;
    }
};
int main() {
    Date d(1,2,3);
    cout<<"& d = "<<&d<<endl;
    Safe dSafe (d);
    cout<<"dSafe.aObj address = "<<&(dSafe.aObj)<<endl;
    return 0;
}

q[explicit] 2 ways to call ctor

A conversion ctor can be invoked 2 ways
B b1 = 123;
B b2 (123);

Similarly, a copy ctor can be triggered 2 ways
B b1 = anotherB;
B b2 (anotherB);

The “explicit” keyword on a ctor disallows the “=” version….

Q(Obscure): why put “explicit” on the no-arg ctor?
A: I see no practical usage. https://stackoverflow.com/questions/48677746/explicit-default-constructor shows an example and some explanation.

struct C{
    explicit 
    C(){}
};
struct K{
    C c;
};
int main(){
    K myk{}; //fails with error: converting to 'C' from initializer list would use explicit constructor 'C::C()'
}

double-checked locking and 3-step constructor re-ordering

I feel one reason why the DCL is broken relates to the 3-step process in “k = new Child()”. In c++, new (delete is similarly 2-stepper) is a 3-stepper — allocation + initialization + pointer assignment. JVM is written in c++, so probably same process —

1) allocate — new address for the new born Child
A) address saves to the 4-byte pointer k. Note the actual “store” operation (ie flush to main memory) may be delayed, but ironically not in this case — When you don’t want it delayed, it may; when you want it delayed it may not … Sigh.
B) initialize the memory cells of Child

Now the surprise — Steps A and B could be reordered by compiler. What if address assignment happens before initialization? Creator thread is in the critical section, between Steps A and B, but 2nd thread outside the critical section already sees A’s effect, believing the entire 3-stepper completed!

You may think Steps A and B are very very adjacent, but no no no. In a 128-processor machine with 100 threads on each, a lot of (mostly bad) things can happen when creator thread is pushed to end of the run queue.

a manager of entity objects #memory leak

(In fact, the paramount justification/motivation of this entire concept is memory leak prevention in C/C++, perhaps in conjunction with a custom Allocator. A java guy may think this “manager” concept sounds cool and impressive but it’s actually less relevant to Java. Like all practical ideas, it started as a simple class with a single purpose and a single field — the pointer. An instance of this class is always an auto-variable on the heap, so it’s AUTOmatically destructed. A non-default dtor deletes the pointer. RAII is one of the simplest yet most effective ways to prevent memory leak.)

You can design a manager for your entity/domain objects. For example, I had a serializer/deserializer for my message objects.

A classic manager is designed to take up responsibility of object creation (like factory), object persistence, object lookup, object destruction, object linking (dependency injection?)…. For overly complex objects, it might be beneficial to separate these lifecycle logic and object manipulation into a manager object, and leave a clean set of behavior inside the domain object.

In contrast, Some designers prefer to consolidate all of these logic in the domain object. A code smell — a well-defined, well-modeled domain model class having methods that return instances of itself. Static or non-static methods, but no factory.

a manager is often a singleton or a “static” thing

== eg of managers ==
* a manager often handles persistence of domain objects. Conversely, I think the same manager can handle object loading from database. Such a manager is not a DAO but a choke point to the DAO layer or persistence layer.

* object validity check — sometimes can be done better by a manager, esp. if that expertise lies in the manager. In one project, i need to check validity of a family of domain objects. If those objects are not derived from each other, then it’s perhaps best to put the validation logic in the manager.

* Factories are managers
* xml bean factory in dependency injection
* EJB container is a manager
* object visibility checker??
* object mutability/read-only checker — error memos read-only checker?
* resource pool is a manager, but objects in it aren’t domain models

new-expression calls op-new and then ctor

See also post on 3-stepper “new” and double-checked locking.

Simple rules
* always — new-expression always includes a constructor call, even for placement new expressions [3]
* never — explicit op-new function call never calls constructor [2]

More rules
* new-expression calls op-new (to grab raw memory), and then ctor (to initialize raw memory), then returns a pointer of the requested type, in one thread
* placement new-expression calls placement op-new, then ctor
* placement op-new doesn’t grab raw memory

http://www.cplusplus.com/reference/std/new/operator%20new/
An expression with the new operator, first calls function “operator new” with the size of its type specifier as first argument, it then automatically constructs the object.

Note [1] some of the steps above could be skipped — Placement version of operator-new (we didn’t say “placement version of new“) does not allocate memory – it simply returns the given ptr. Notice though that the constructor for the object (if any) will still be called by the operator expression i.e. the new-expression [3].

Note [2] operator-new function call PRECEDES (not includes) constructor call. If you call operator-new explicitly, no constructor called!

   myclass * p3 = (myclass*) operator new (sizeof(myclass));
// (!) not the same as: myclass * p3 = new myclass;
// (constructor not called by explicit operator-new call)

Note [3] op-new can include a 2nd argument --
new (p3) myclass; // placement-new-expression, calls constructor
// similar to --
operator new (sizeof(myclass),p3)
// placement-op-new. no constructor call


http://www.cplusplus.com/reference/std/new/operator%20new%5B%5D/ shows simple examples of operator-new[] including the placement version

operator new[] can be called explicitly as a regular function, but in C++, new[] is an operator with a very specific behavior: An expression with new and an array type specifier is an operator new[] expression. This first calls function operator new[] with the size of its array type specifier as first argument (plus any array overhead storage to keep track of the size, if any), and if this is successful, it then automatically initializes or constructs each of the objects in the array (if needed). The expression evaluates as a pointer to the appropriate type pointing to the first element of the array.

— jargon warning
ARM says
“the new operator will call the function operator new()”. it means
The new expression will call the function operator new()

scope-exit destructor call

When you exit a scope, the object(s) created on the current stack frame is destroyed (except RVO — see http://bigblog.tanbin.com/2010/09/return-value-optimization.html).

* stackVars have matching var scope and object life time so they are destroyed.
* pbclone params — are just like stackVars
* pbref params — variable has scope but the object is not on the current stack frame and has a longer lifetime. Not destroyed.
* address passed in — the param is like a local pointer var. This var is destroyed when it goes out of scope, but the pointee is and should not.
* heapy-thingy instantiated locally — the pointer var is an auto/local/stackVar — so destroyed when it goes out of scope, but the pointee is not! This is a classic memory leak. In this case, whoever calling new should call delete.

c++ big 3 when subclassing

Update — new best practice using smart pointers would have mostly trivial or empty implementations of the big3.

See post on [[virtual — only 1 of the big 3 please]]

When you create a


class C : public B

copier — Please call B copier in C copier’s initializer
assignment — Please call B assignment among first lines in C assignment overload
dtor — no need to call B’s dtor. When reclaiming C , compiler guarantees to reclaim the B part of the C object real estate, but imagine a ptr field in B points to a 22 byte object. The 4-byte ptr is in the C object real estate, but not those 22 bytes. Compiler guarantees to reclaim the 4 byte ptr but you must make it reclaim the 22 bytes.

Note most of B’s public methods are inherited, but not big 3. These 3 methods wouldn’t do a good job if inherited, as they need access to subclass fields.

Just as in java, constructors aren’t inherited. You can inherit everything from your mother, but not her date of birth.

pbclone always uses pbref (possible slicing)

I always thought a function entry/exit is either pbclone (pass-by-value) or pbref (pass-by-reference) but never both. Now I know a pbclone always[1] uses a pbref somewhere.

Special case — if a copy ctor parameter is missing the “&”, then the pbclone will call the same ctor pbclone recursively — infinite recursion and stack overlow.

Note standard c++ copier has signature (MyClass const & rhs) . So the original arg object is first passed by reference into the copier. Occasionally this is non-trivial.

What if the arg object is a child of MyClass? Slicing ! The rhs variable can’t access the fields added by the child class.

[1] except the trivial case where the the argument is not an object but a literal

copier & asignment — part of the fabric

When you overload the assignment operator carelessly, you may trigger 1 or 2 implicit calls to the copy constructor. Compared to assignment, copy constructor is even more implicit, more frequently called, more part-of-the-fabric.

The pattern starts with C pbclone (pass-by-clone aka pass-by-value) convention. All java primitives (and c# value types?) are pbclone. Every object passing in and out triggers the copy constructor.

Similarly, C++ class types are pbclone by default, unless you specify references in the func prototype[1]. In an overloaded assignment operator, there is exactly one pass-in and one pass-out, so a careless overload triggers exactly 2 copy constructor calls.

Actually, even more part-of-the-fabric than copier is pbref, because the default copier has signature (MyClass const& rhs).

[1] either the return type or the param types, or both

copy-constructor^destructors @ function border (pbclone)

in a simple[2] C function, when you pass (in or out) an integer, you create a copy. Local variables go out of scope at end of blocks {defined by braces}. Out of scope means memory reclamation. Pass-by-clone means memory allocation.

in C++, all user defined objects are treated LIKE the integer above. When passing in (or returning), the copy-constructor creates a copy (P180 [[24]]). Local variables go out of scope under the destructors.

[2] no pointers please.

calling assignment from copier (UBS IV

#include
// Using “this” in contructor? See http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.7
using namespace std;
struct CAT {
    string name;
    int age;
    CAT& operator=(CAT const & rhs) {
        cout << "entering operator= " << this << " <– " << &rhs << "\n";
        this->name = rhs.name;
        this->age = rhs.age;
        return *this;
    }
    CAT(CAT const & rhs) {
        cout <name << " is current name\n";
        cout <age << " is current age\n";
        *this = rhs; // using operator=
    }
    CAT() {}
};
int main() {
    CAT e;
    e.name = “Thomas “;
    e.age = 33;
    CAT copy(e);
    cout << copy.name << copy.age << endl;
    cin.get();
    return 0;
}

c++ virtual ctor #clone()

P263 ARM has a simple eg. Scott Meyers covered it too. Here are a few tips to help you remember some of the details.

virtual B * clone(); // declare

Q: take any argument?
AA: no-arg. The only input to the cloning is “this”  — The host object’s this-pointer

Q2: Return by value or reference? Has to be reference IMO. How about rvr?

Q3: Return type?
AA: covariant return type. since each derived class’s implementation instantiates an object of a distinct type. I wrote a separate blog post about this.

Q4: pointer to heap or stack?
%%A: stack means imminent destruction, so must be heap.

Q5: who will delete it?
%%A: you can use a smart ptr to automate the deletion. I feel sometimes it’s not possible, so the calling function should be responsible to delete it.

parent dtor always called; field dtor always called@@

When a C object (subclass of B) is destroyed, compiler guarantees to clean up the entire real estate of the C object, which always always includes a B real estate. Therefore the B dtor is never bypassed. (Same deal with B constructor.)

Q: Now what if B has a ptr/ref to a heap object?
A: the heapy thingy is NOT in B real estate
A: Say the ptr field occupies 4 bytes of B real estate. The 4 bytes are reclaimed, but not the heap pointee object. You need to customize dtor to clean up heap.

new-expression calls op-new — implicitly

op-new is officially a function, and always requires 1 or more arguments.

– new MyType implicitly calls qq/…operator new(sizeof(MyType)) /
– new (2,f) MyType calls qq/..operator new(sizeof(MyType), 2,f) / — Note the (2,f) are placement arguments. See http://en.cppreference.com/w/cpp/language/new

– if arguments come After “MyType”, then they are ctor arguments, like qq/ new int(7) /

Actually,
– new MyType implicitly calls qq/ C::operator new(sizeof(MyType)) / if any, and qq/ ::operator new(sizeof(MyType)) / otherwise

destructor,delete,free()–overview+details

Delete invokes pointee dtor [1] implicitly b_u_t dtor never implicitly calls delete.

[1] See P64 ARM.

I feel delete is a 2-stepper — destruction and reclaim. Reclaiming the heap space of the pointee, not any real estate outside the pointee.

“Reclaim” is more accurate than “free”. Reclaim means this real estate could belong to new objects. Even reading it could trigger disasters.

Now, suppose your object has a pointer field. You should program your dtor to call delete. Default dtor never calls delete. Dtor bulldozes the real estate of the host object. A 4-byte pointer lives on that real estate, but the pointee object lives somewhere on heap.

^ delete reclaims pointee’s heap space, but the 4-byte pointer is not reclaimed. People can still use this pointer field/stackVar, that’s why we need to set it to null.
^ in contrast, dtor bulldozes the 4-byte pointer field but doesn’t touch the heap real estate of the pointee.

For a smart pointer, dtor and operator delete are somewhat decoupled. “Coupled carefully” is how i put it. Smart pointer must carefully decide when to call operator delete.

At a high level, delete is responsible for reclaiming HEAP memory of a pointee; destructor is responsible for bulldozing real estate (stack or heap) of host object.

Another sound byte —
default dtor never calls delete on a ptr *field*;
default dtor implicitly calls dtor of fields and parents; P277 ARM.
container dtor implicit calls dtor of elements. See P36 eff STL

no throw from destructors please

http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.3 is a short and clear explanation. Here are my comments:
No throw please until you catch the original exception. In fact, Most if not all the methods in exception class are declared with an empty throw()

——
The C++ rule is that you must never throw an exception from a destructor that is being called during the “stack unwinding” process of another exception. For example, if someone says throw Foo(), the stack will be unwound so all the stack frames between the throw Foo() and the } catch (Foo e) { will get popped. This is called stack unwinding.
During stack unwinding, all the local objects in all those stack frames are destructed. If one of those destructors throws an exception (say it throws a Bar object), the C++ runtime system is in a no-win situation: should it ignore the Bar and end up in the } catch (Foo e) { where it was originally headed? Should it ignore the Foo and look for a } catch (Bar e) { handler? There is no good answer — either choice loses information.
So the C++ language guarantees that it will call terminate() at this point, and terminate() kills the process. Bang you’re dead.
The easy way to prevent this is never throw an exception from a destructor. But if you really want to be clever, you can say never throw an exception from a destructor while processing another exception. But in this second case, you’re in a difficult situation: the destructor itself needs code to handle both throwing an exception and doing “something else”, and the caller has no guarantees as to what might happen when the destructor detects an error (it might throw an exception, it might do “something else”). So the whole solution is harder to write. So the easy thing to do is always do “something else”. That is, never throw an exception from a destructor.
Of course the word never should be “in quotes” since there is always some situation somewhere where the rule won’t hold. But certainly at least 99% of the time this is a good rule of thumb.

(badly) implicit ctor calls — var declaration

Q: When do c++ constructors get invoked? Harder to tell than in java.

MyClass a; // calls noarg, behaves exactly like int/float and other builtin types

a = MyClass(); // calls noarg, assignment, and destructor.
// New object created on stack then duplicated in a’s memory location, field by field. New object is then discarded as a STACK var.
// I believe the parentheses are compulsory after the class name.

MyClass arr[9]; // nine constructor calls. no-arg constructor needed! Note the position of [].

MyClass b(a); // calls copier ….. [2]

C c3 = c1; // calls copier, not assignment. same as [2]. Disabled by “explicit” keyword on the copier ctor

C c2;
c2 = c1; // calls assignment, not copier

////// So far, all the variables are nonref stack vars.

MyClass b(); // parsed as a function prototype, not a constructor call

MyClass * ptr; // confirmed no constructor call. The pointer is uninitialized.

MyClass * ptr = 0; // ptr starts off as uninitialized, then it is initialized to 0 i.e. null. You can see these 2 steps by debugging.

assignment overloading && other op overloading

Notice many overloaded operators return a value to be used as RHS of assignment. It’s good to become thoroughly familiar with this interaction — [ op= and overloaded operators ]

When you assign returned value of any (pbclone/pbref) function, you get op= . op= simply reads the RHS, and doesn’t care if the func is pbclone or pbref.

protected virtual dtor #tricky

[[boost]] P 24 explains the technique of protected virtual destructor. Here’s my take —

The superclass A destructor is called by B’s destructor. See http://www.cplusplus.com/forum/general/12344/. Remember you first clean up the outermost layer of the onion, and remember the B fields exist on the outer layer, outside A’s real estate.

Now, if someone gets hold of an A pointer, where the pointee is B, she can’t call delete on the pointer. Compile time (not run time) error. The special dtor is a protection.

class Base{
  protected: virtual ~Base(){cout <<"base dtor" <<endl;}
};
class Derived : public Base {
  public: virtual ~Derived(){cout <<"derived dtor" <<endl;}
};
int main(){
  Base * p1 = new Derived;
  Derived * p2 = new Derived;
  delete p1; //won't compile
  delete p2; // ok
}

If I swap the 2 words public and protected then I can delete Base ptr but not Derived ptr (compiler error)!

So it seems declared type must have a public dtor. In our examples, even though object is Derived, delete is allowed only on the ptr whose declared type has a public dtor.

Now, What if i have a private dtor? Probably less common, but it’s possible to invoke this private virtual dtor —

class Base{
  public:virtual ~Base(){cout <<"base dtor" <<endl;}
};
class Derived : public Base {
  private: virtual ~Derived(){cout <<"derived dtor" <<endl;}
};
int main(){
  Base * p1 = new Derived;
  delete p1; // ok private destructor called!
}

In my experiments, qq(new) is mandatory as a stackVar or this class will trigger a compiler error, because compiler can’t destroy the object automatically.

assigning^constructing a c++obj: a few diff

Assignment operator is a field-by-field and bitwise overwrite [1] without eating up new memory. Both LHS and RHS must be (preexisting) objects with addresses.

A constructor always eats up (heap or stack) new memory, and reduces free memory.

[1] If a field is a pointer, then the same address is put into the LHS’s field bitwise. LHS and RHS both point to the same address — shallow copy.

Q: what if a field is a reference?
A: Can’t reassign. Default op= will trigger an error (compile time or runtime).
A: reference field is rare.

##destructor is event-driven — what events@@

Destructor fires when…

* AFTER subclass destructor [ISA]
* stackVar going out of scope (automatically destroyed — A U T O variable
** special case — exceptional stack unwinding
* delete
** special case — cascading delete — Host object destructor [HASA]

Now, in the ISA situation all destructors should be virtual, so how does the parent destructor fire? Boris told me child destructor doesn’t need explicit call to parent destructor. System guarantees all destructor fire from descendants to ancestors.