C++ Primer learning notes #2

C++ Learning notes

Introduction

I write down this succession of notes for the purpose of developing my English ability&habit while learning C++. Therefore I make all the notes in English.
If you have any reap or advice, don’t hesitate to comment(with any language). If you find some bugs in my notes or sentences or grammar, please let me know(granted my English is very poor). If you are not used to reading notes in English, just close this page and browse another note.

overloading and type conversion

basic concept

  • an operator function should be a member of a class or have at least than one class type parameter
  • which means we can’t overload operators acting on the build-in type
  • overloaded operator’s priority and associative law is the same as the original operator

operators can be overloaded

+-*/%^
&|~!,=
<><=>=++
<<>>==!=&&||
+=-=/=%=^=&=
|=*=<<=>>=[]()
->->*newnew[]deletedelete[]

operators can not be overloaded

::.*.? :

operators

there are various of operators overloading practice. and you know, cujus rei demonstrationem mirabilem sane detexi. Hanc marginis exiguitas non caperet.(I’m sure I’ve discovered a wonderful demonstration, but the space here is too small to fill it). so I write them all down in my practice .cpp file and just note some announcements there.

The essence of operator overloading is a function call. therefore we can’t save the original order of evaluation and the short circuit evaluation. and that’s why we don’t advice you to overload comma, address, logic and, logic or.

when we define an operator as a member function, its left operand must be an object of the operator’s class.

operator(=), index([]), call(()), member accession(->) operator must be member function of class.

ostream(<<), istream(>>) can’t be member function.

index operator usually define two overloading versions, the const one and the nonconst one.

increase and decrease operators usually be set as member functions.

strBlobPtr &assignment++();		//preassignment
strBlobPtr assignment++(int);		//postassignment

strBlobPtr strBlobPtr::assignment++(int) {
	strBlobPtr ret = *this;
	++*this;
	return ret;
}

if we overload the call operator, the object of this class is called function object, because we can call this object. function object is usually used as actual parameter of generic algorithm. moreover, lambda is an unnamed class’ unnamed object, whose call operator is overloaded by lambda’s function body.

liberty functional defines a succession of function class as templates. and they are listed below.

arithmeticrelationlogic
plusequal_tological_and
minusnot_equal_tological_or
multipliesgreaterlogical_not
dividesgreater_equal
modulus1less
negateless_equal

operator type() const is class-type conversions or be named user-define conversions. it allows a type to be an object’s return type. this conversion is implicit.

we can define explicit conversion operator to control class-type conversion.

class SmallInt{
	public:
		SmallInt(int i = 0): val(i) {}
		explicit assignment int() const {return val;}
		assignment=(int);
	private:
		int val;
};

SmallInt si = 3;
static_cast<int>(si) + 3; //explicitly request for type conversion

however, if the conversion is used as a condition, the compiler will automatically execute the conversion.

function table

function, function pointer, function class, lambda expression, classes built by bind and any other classes which has overloaded the call operator are all callable object. function class defined in liberty functional can be used to reserve callable object.

#include <functional>

function<int(int, int)> f1 = add;
//f1(4,2) == 6; //equal to add(4,2);
//f1 == true;	//f1 has a callable object;
function<int(int, int)> f2 = [](int a, int b){return i * j;};

we can define a function table to reserve callable objects with same retType(args).

map<string, function<int(int,int)>> binops = {
	{"+", add},	//each element is a pair
	{"-", minus<int>()},
	{"/", divide()},
	{"*", [](int i, int j){return i * j;}},
	{"%", mod},
};

//in this case, binos["+"](4,2) == 6;

Object-Oriented Programming

inheritance

class Quote {
	public:
		Quote() = default;
		Quote(const std::string &book, double sales_price):
			bookNo(book), price(sales_price) {}
		std::string isbn() const {
			return bookNo;
		}
		//key word virtual defines a virtual function
		//which means the base class permits drived classes to override it
		virtual double net_price(std::size_t n) const {
			return n * price;
		}
    	//moreover, if we don't wish our clients use the base class' member function
    	//we need to define this member function to be a pure virtual function as the following code row
    	//notice :the pure virtual function can only be defined in the declaration body
    	virtual double net_price(std::size_t n) const = 0; 
		virtual ~Quote() = default; //dynamically bind destructor
	private:
		std::string bookNo;
		/*
		*key word protected serves for those members
		*which are not supposed to be used by object's users
		*but needed to be used by derived class
		*/
	protected:
		double price = 0.0;		//common price without discount
};

/*
*type of class derivation list:colon and a list of base class
*whose type is base class behind optional assess specifire
*and splited by comma
*/
class Bulk_quote: public Quote {
	public:
		Buli_quote() = default;
		Built_quote(const std::string &, double, std::size_t, double);
		/*
		*key word override explicitly declare that this member function
		*will be used to redefine the base class' virtual function
		*if not, the compiler will throw error
		*/
		double net_price(std::size_t) const override;
	private:
		//the minimum purchase quantity of discount
		std::size_t min_qty = 0;
		double discount = 0.0;
};

because derived class has its base class member, compiler can implicitly convert it into its base class as a reference or pointer. On the contrary, compiler doesn’t allow us to implicitly convert base class object into derived class object. However, if we ensure this action’s safety, we can use static_cast enforce the conversion. But this action is not recommended. What’s more, if the base class has at least one virtual function, we can use dynamic_cast to check this conversion’s safety and let the compiler divides whether allow or not.

if a base class defines a static member, the whole inheritance system has only one instance no matter how many derived classes and objects have been defined.

base class must be defined before defining derived classes.

key word final restrict us from using a class as base class. key word final can be used to declare that a virtual function is the final function, which means this function can’t be overrode.

class NoDerived final {/*details*/}
//illegal, we can't use Noderived as a base class
class Bad: public NoDerived {/*details*/}

virtual function can have acquiescent parameters, and its acquiescent parameters depend on the static type of the call.

class Base{
	public:
		virtual void funcion(int a = 0, int b = 1){/*details*/}
}
class Derived: public Base{
	public:
		void function(int a = 1, int b = 2) override{/*details*/}
}

void execuse_function(Base &b){
	b.function();
}

int main(){
	Derived d;
	execuse_function(d); //execuse Base::function(int a = 0, int b = 1);
}

scope

notions:

  • iff the derived class publicly inherits base class can users use the conversions between derived class and base class.
  • if the derived class publicly or protected inherits base class, the derived class can use the conversions.
  • if the derived class privately inherits base class, the derived class’ objects and friends can’t use the conversions.
  • friend relationship can’t be inherited.
  • each class controls its members’ accessible permissions.
  • if we need to change a member’s accessible permission, we can use the using declaration in the corresponding action scope
class Base{
	public:
		std::size_t size() const {return n;}
	protected:
		std::size_t n;
}
//we inherit the base class in the private scope
class Derived: private Base{
	public:
		//then Base::size() is public 
		using Base::size;
	protected:
		using Base::n;
}

acquiescent derived scope operators depend on class’ type. struct acquiescently owns public scope while class having private scope.

derived class’ scope is combined into base class’ scope. thus derived class’ member with the same name as base class’ has higher priority.

name lookup precedes type lookup. function declared in the inner scope won’t override but cover the outer scope’s function.(which remind us to use the override key word. otherwise we must ensure that the derived class’s function has the same parameters list as the base class’)

struct Base{
	int memfunc();
}

struct Derived: Base{//Base's scope assignment is public
	int memfunc(int);
}

Derived d;
Base b;
b.memfunc();
d.memfunc(10);
d.memfunc();		//error:Base::memfunc() is hided. the right way is written below
d.Base::memfunc();

dynamic binding

double print_total(ostream &os,
                   const Quote &item, size_t n) {
	//calling Quote::net_price or Bulk_quote::net_price
	//depends on item's object type
    //moreover, accessible members which we can use depend on the static type
    //for instance, this item below has no right to use Bulk_quote's unique public members
	double ret = item.net_price(n);
	os << "ISBN: " << item.isbn()
	   << "# sold: " << n << "total due: " << ret << endl;
	return ret;
}
//if we don'y want to use dynamic binding, we need to explicitly declare the scope
double ret = item->Quote::net_price(42);

now, we need to consider this situation where we delete a derived object pointed by a base class pointer. we must ensure that the delete command executes the derived object’s destructor. So we declare the base class’ destructor as a virtual function to solve this problem. what’s more, virtual destructor will impede generating movement operation.

What’s more, if we use containers to save objects, it is supposed to indirectly save it. An common usage is using pointer. Then we are able to use base class pointer calling derived objects.


  1. 取模量 ↩︎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值