evazkj
11/1/2018 - 6:12 PM

C++ core

MISC about c++ core

functor

  • Usually inline
  • Usually only use temporary object.
  • Normally passed by value
  • Can have state
  • The function operator should be const

Overloading

  • About overload == operator:
class Transaction {
  public:
    // Equal if both the volume and price are equal
    bool operator==(const Transaction& t) const 
    {
        return (d_volume == t.d_volume) 
         && (d_price == t.d_price);
    }
    bool operator!=(const Transaction& t) const 
    {
        return !(*this == t);
    }
};
  • Member and non-Member implementation
class Transaction {
    ...
    bool operator==(const Transaction& rhs) const;
};

Or

bool operator==(const Transaction& lhs,
                const Transaction& rhs);
  • Best practice: If you can implement the operation as a non-member, non-friend function, do so Otherwise, use a non-member function to implement an operator if you want implicit type conversions to apply to both operands If the non-member function requires access to private members, it must be a friend of the class Otherwise, use a member function

It's best that we also overload != when we overload ==.

Implicit type conversion, casting

  • see the difference:
    • T a(12): constructor
    • T b = 12 is equivalent to: T tmp(12); b = temp; temp ~T;
  • In the implicit type conversion, we called two things:
    • a Constructor
    • a copy operator
    • a decontructor
  • To block this kind of implicit conversion, we can disable the constructor by adding keyword explicit
  • Example of conversion:
class X {
  public:
    X(A);               // Conversion from A to X
    X(const B&);        // Conversion from B to X
    explicit X(C);      // No implicit conversion
                        // from C to X
    operator D() const; // Conversion from X to D, a bit dangerous
    D toD() const;      // this may be a better way to write conversion
    ...
};
  • Frequent casting is not a good sign! Such as:
int * p;
p = (int*) 123;
  • C++ has four cast operators: static_cast<type>(expression) const_cast<type>(expression) dynamic_cast<type>(expression) reinterpret_cast<type>(expression)

  • Cast is very powerful but is very dangerous, which can be legal but cause run-time errors.

Copy Constructor & Copy Assignment Operator

  • C++ provides shallow copy constructor for free:
class T { 
  public:
    T(const T& rhs);
}
  • C++ distinguishes a copy constructor with a assignment.
  • The compiler-generated copy constructor copies each data member
    • For class/struct data members, it calls that class’s copy constructor
    • For intrinsic types, it does a bitwise copy (it just copies the bits) A bitwise copy is fine for int, char, double, etc.,
    • For pointers, this means the source and target pointers will be the same, i.e., will point to the same thing This is usually not what we want!

Deconstructor

  • Compiler-generated Deconstructor:
    • The compiler-generated destructor calls each data member's destructor.
    • The members are destroyed in the reverse order of their declaration
    • Does nothing for primitive types (e.g., char, int, char *)
    • Is usually what you want for non-pointers. But not for pointers ......therefore, you should always define your own destructor if your class dynamically allocates memory (via new)

Assertion

  • Three levels of assertions
    • BSLS_ASSERT_SAFE(condition) Condition is expensive to evaluate, greater big O than the function itself
    • BSLS_ASSERT(condition) Condition is inexpensive to evaluate; default assert to use
    • BSLS_ASSERT_OPT(condition) Negligible cost of evaluation and great risk if ignored
  • Programmer can dynamically change the behavior triggered by failed assertions
    • bsls_Assert::setFailureHandler(&bsls_Assert::failThrow);

Install an alternate assertion-failure handler function

  • Assertion can be shut off in production by modifying the .mk file.

  • Can be overridden by an optional assertion-level flag Define the flag (macro) using the -D option,

    • USER_CPPFLAGS += -I. –D ...
    • One of:
    • -D BSLS_ASSERT_LEVEL_NONE
    • -D BSLS_ASSERT_LEVEL_ASSERT_OPT
    • -D BSLS_ASSERT_LEVEL_ASSERT
    • -D BSLS_ASSERT_LEVEL_ASSERT_SAFE Perform a clean build
  • Assertion must have no side effect!

  • In C++ 11, we have "static_assert", that does assertion in compile time. e.g.

    • static_assert(sizeof(int) == 4)

Passing array to a function

  • void foo(int arr[]); // … or …:
  • void foo(int *arr);
  • Both declarations mean the same thing, however you should be careful about communicating your intention: int arr[] makes it clearer the function expects an array argument
  • Do not use the following declaration:
    • void foo(int arr[10]) because compiler doesn't check size, it is dangerous.
  • Best practice:
    • void foo(int arr[], size_t size);

Memory allocation

  • When new signal fails:
while (true) {
    // leak memory until we die...
    try {
        int *ip = new int(211);
    } catch (const bsl::bad_alloc& e) {
        bsl::cerr << "I'm exhausted!"
                  << e.what() << "\n";
        break;
    }
}

About const non-const conversion

foo(const )

Sequance point

In C++, defines any point in a computer program's execution at which it is guaranteed that all side effects of previous evaluations will have been performed, and no side effects from subsequent evaluations have yet been performed.

In f()+g() it is possible that either f() or g() will be executed first.

So be very careful when you write one expression thet contains multiple function whose behaviors are dependent with each other.

Another example:

cout << stack.pop() << stack.pop() 

The interpreter may evaluate the second one first. It's called ambiguity.

Another famous example is i = i++.

Sequence point examples:

  1. Logic operand: &&, ||. The expressin on the left will be executed before right.
  2. The select operand: ?.
  3. Before a function is entered in a function call. The order of the arguments being evaluated is unknown, but all arguments will be evaluated before entering the function

Pointer:

  • void* is not “a pointer to nothing” but rather “a pointer to anything”. It is a type supported in C and C++ to hold the value of any pointer, regardless of target type. Any address can be stored in a void*, however, they cannot be dereferenced unless first cast into the correct pointer type.
int i = 10;
void *pi = &i;
*pi = 5; // ERROR
*static_cast<int*>(pi) = 5; // legal, but dangerous
*((int *) pi) = 5; // equivalent using "C-style" cast

DO NOT USE VOID POINTER !!!

const

  1. When an object is sent into a function as const, all its members will be const. For example, if we get a int foo(const vector<int>). If the function returns a member of vector it will be const too.

Common place that needs const

  1. print function:
&bsl::ostream operator<<(bsl::osteam& out, const CLASS object&)

Inline function:

  • Pros: No function call overhead.

Inlining may give the compiler more optimization opportunities

  • Cons: Can contribute to code bloat (see Meyers, item 30).

Definition must be visible to clients—not just the declaration; inlines are usually defined in header files Generally cannot set a breakpoint at or in an inlined function

namespace {
using libnlp::attr::TokenClassTag;
using libnlp::attr::TokenClassTagList;
using libnlp::core::Algorithm;
using libnlp::core::AnnotationList;
using libnlp::core::Document;
using libnlp::util::TypeList;
} // namespace

What happens:

int& foo(int a){
  return a;
  }
  • Always check null when a function is fed a pointer
  • In bloomberg, if we want to make changes inside something, we pass pointer, if we just want to check something, we pass const reference.
    • An exception is if we want to change make a pointer point to another object, we need to pass a reference to a pointer.
  • If you defined a object with a pointer in it, you need to define your own deconstructor.
  • The best way to delete a variable defined by:
cls **ptr = new cls*[10]
delete [] ptr; //destroy all elements in the array
delete ptr; // destroy the array itself
  • valgrind --leak-check=yes ./*.tsk can be used to check memory leak.
// Read definitions from right to left
T ** a;
T * const *a; // pinter of a const pointer to a T
T const** a; // pointer of pointer to a const T
const T **a; // pointer of pointer to a const T

int (*const a)[3] // an array of 3 const pointers that points to T
int *(*a)[3] // a is a pointer to a array of 3 int pointers
// the parenthesis is to defined the following [3]

Segment faults

  1. Two pointers point to the same dynamicly allocated object, but one of them delete the memory. The other one will go invalid as well.
  2. A not well-defined copy constructor and copy operator!
  3. Casting. For example trying to cast a const variable to a nonconst variable.