Essential c++初学 第四章 basic c++ programming

本文深入探讨C++中的构造函数与析构函数,解释它们的作用与应用场景,包括成员初始化、拷贝构造、const使用、this指针、静态成员函数、运算符重载及友元函数等内容。

1.构造函数constructor和析构函数destructor
构造函数在函数定义后立刻执行,相当于是一种初始化过程,在object被实例化之前就执行。构造函数的函数名和类名相同(相当于是自己定义自己)

//e.g.
class Triangular{
public:
//...
private:
int _length;
int _beg_pos;
int _next;
int *_pmat;
}
//冒号后面的_name是成员初始化列表,直接赋予private初值
Triangular::Triangular(int len,int bp):_name("Triangular")
{
_length = len>0? len,1;
_beg_pos = bp>0? bp:1;
_next = _beg_pos -1;
}
inline Triangular::~Triangular()
{	//destructor常用来做资源释放
	delete[]_pamt;
}

destrctor功能相反,绝不会有返回值也没有任何参数,由于参数列表是空的,因此也绝不可能被重载。常用来做资源释放。

2.成员逐一初始化
有时我们需要以某个class object作为另一个object的初值,例如:
Triangular tri1(8);
Triangular tri2 = tri1;
本例中的_length,_beg_pos,_next都会依次从tri1复制到tri2,这就是默认的成员逐一初始化。
但如果类中间有destructor析构函数,数组之类的数据容器就会被释放掉,但可能私有值的指针仍然指向那里,这样就会出错。

//e.g.
#include "Triangular.h"
 Triangular trian(8);
 //此时,constructor发生作用
 {Triangular trian2 = trian;
 //此处,进行default memberwise initializetion
//...在这里使用trian2
//此处trian2的desturctor发生作用
}
//...在这里使用mat
//此处,trian的destructor发生作用

解决方法:产生一个独立的数组副本,这样就可以对某个对象进行析构操作又不至于影响到另一个对象

//e.g.
Triangular::Triangular (const Triangular &rhs):_length(rhs._length),_next(rhs._next)
{	//对rhs._pmat所指的数组产生一份完全副本
	int elem_cnt  = _length * _next //我乱写的关系式,使用时满足正确的关系式即可
	_pmat = new double [elem_cnt];//new double 动态分配内存大小数组容器.
	for(int ix = 0;ix<elem_cnt;++ix)
	_pamt[ix] = rhs._pmat[ix];
}

3.可变和不变 (const的用法)

比如说我们进行一个某个数列各元素的求和,我们必须保证trian在sum()中不会被修改,然而任何一次member function的调用都可能会更改trian的值,所以我们要在calss public中使用const

int sum(const Triangular &trian)
{
	int _beg_pos = trian.beg_pos();
	int length = trian.length();
	int sum = 0;
	for (int ix =  0; ix<length;++ix)
		sum += trian.elem(beg_pos +ix);
		return sum;
}

根据sum来定义类Triangular

//e.g.
class Triangular{
public:
//以下是const member function
int length() const {return _length;}
int beg_pos() const{return _beg_pos;}
int elem(int pos ) const;

//以下是non-const member function
bool next(int &val);
viod net_reset(){_next = _beg_pos -1;}
private:
int _length;//元素个数
int _beg_pos;//起始位置
int _next;//下一个迭代目标

//static data member
static std::vector<int>_elems;
};

在定义const时,必须考虑到member function完全不更改class object的任何值

4.This 指针
是一个指向对象的指针
this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。

this 作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。


Triangular tr1(8);
Triangular tr2(8.9);
tr1.copy(tr2)//把tr2的各项数据传给tr1

//e.g.  以一个对象复制出另一个对象
Triangular & Triangular::copy(const Triangular &rhs)
{//检查两个对象是否相同
	if(this != &rhs)
{
		_length = rhs._length;
		_beg_pos = rhs._beg_pos;
		_next = _rhs._beg_pos -1 ;
}
	return *this;
}

上例中,this是指向tr1的指针,*this则是返回所指的对象本身。

5.静态成员函数
直观理解:静态成员函数时一种与类紧密连接的全局变量,让某种属性或算法称为该类(多类也是可以的)所共用的,一般来说,类内声明一次后,类外要定义/初始化一次。

//Triangular.h
#pragma once
#include <string>
#include <vector>
#include <iostream>

class Triangular
{
public:
	Triangular(int len,int beg_pos);
	//结构函数
	static bool is_elem(int elem);
	static void gen_elements(int length);
	static void gen_elems_to_value(int value);
	//static void display(int length, int beg_pos, std::ostream &os = std::cout);


private:
	std::string _name;
	int _next, _length, _beg_pos;
	
	static std::vector<int> _elems;//静态成员只有唯一实体
	static const int _max_elems = 1024;
};

std::vector<int> Triangular::_elems;//类外定义一次

//member intialization list 成员初始化列表 :后表示给某些项赋值
Triangular::Triangular(int len, int bp) : _name("Triangular") {
	_length = len > 0 ? len : 1;
	_beg_pos = bp > 0 ? bp : 1;
	_next = _beg_pos - 1;
}

bool Triangular::is_elem(int value) 
{	
	if (!_elems.size() || _elems[_elems.size() - 1] < value)
		gen_elems_to_value(value);

	std::vector<int>::iterator found_it;
	std::vector<int>::iterator end_it = _elems.end();

	found_it = std::find(_elems.begin(), end_it, value);

	return found_it != end_it;
}

void Triangular::gen_elems_to_value(int value) 
{
	int ix = _elems.size();
	if (!ix) {
		_elems.push_back(1);
		ix = 1;
	}
	while (_elems[ix -1 ]<value && ix<_max_elems)
	{
		std::cout << "elems to value:" << ix*(ix + 1) / 2 << std::endl;
		++ix;
		_elems.push_back(ix*(ix + 1) / 2);
	}
	if (ix == _max_elems)
		std::cerr << "value too large" << value << "exceeds max size of " << _max_elems << std::endl;

}

void Triangular::gen_elements(int length) 
{
	if (length<0 || length>_max_elems) {
	//发出错误信息然后直接返回
	}

	if (_elems.size() < length) 
	{
		int ix = _elems.size() ? _elems.size() + 1 : 1;
		for (; ix <= length; ++ix)
			_elems.push_back(ix*(ix + 1) / 2);	
	}
}
//cpp程序
#include "Triangular.h"
#include <string>
#include <iostream>
#include <vector>

int main() {
	char ch;
	bool more = true;
	while (more)
	{
		std::cout << "enter value:";
		int ival;
		std::cin >> ival;
		
		bool is_elem = Triangular::is_elem(ival);
		std::cout << ival << (is_elem ?  "is"  :  "is not " )
			<< "an element in the Triangular series.\n"
			<< "Another value?(y/n)";
			std::cin >> ch;

			if (ch == 'n' || ch == 'N')
				more = false;
	}
}

6.重写运算符

#pragma once
#include <vector>
#include <iostream>

class Triangular
{
public:
//...
	friend std::ostream & operator<<(std::ostream& os, const Triangular& t);

private:
//...
};
std::ostream& operator<<(std::ostream& os, const Triangular& t) {
	os << "(" << t.length() << "," << t.beg_pos() << ")";
	return os;
}
//...

7.友元函数
一个类为了方位另一个类的私有,可以通过友元函数的形式(或者直接在public里面写一个专门的函数直接返回私有,这样别的类就可以通过调用函数的形式返回了)。

友元函数有两种形式,一种是声明一个函数是friend性质的。例如

//e.g.
class Triangular{
private:
friend int Triangular_iterator::operator*();
friend void Triangular_iterator::check_integrity();
}

上例中,Triangular_iterator要访问Triangular的私有,所以在Triangular中声明Triangular_iterator的这两个函数是他的朋友,朋友就可以访问私有的。

第二种形式,直接声明一个类是另一个类的私有。

class Triangular{
private:
friend class Triangular_iterator;
}

8.定义operator
如果想定义一个操作符,需要在操作符之前加上operator关键字。以下将以复制(即等于号,把A的对象a的值赋给对象b)

Matrix& Matrix:: operator= (const Matrix &rhs)
{
	if (this != &rhs) 
	{
		_row = rhs._row; _col = rhs._col;
		int elem_cnt = _row *_col;
		delete[] _pmat;
		_pmat = new double[elem_cnt];
		for (int ix = 0; ix < elem_cnt; ++ix)
			_pmat[ix] = rhs._pmat[ix];
	}
	return *this;
}

其中matrix&是返回类型, matrix::operator代表我定义的操作符是matrix这个类的,等于号相当于函数名,接收一个右侧参数,都是以传地址的方式来输入输出的。最后返回this指针所指向的对象。
*this返回rhs(输入右值)的长度和起始位置。(因此也是个地址,所以一开始要写matrix&),this等于说是个指向地址的指针。

9.创建一个function object运算符
当编辑器遇到形如
lt.(ival)时,这个lt可能是个函数名称,可能是函数指针,也有可能是个提供function call运算符的function object。如果是这样,那么编辑器会在内部将此语句转化为
lt.operator(ival);
具体例子如下:

//e.g.
//LessThan.h头文件
#pragma once
#include <vector>
#include <iostream>
#include <algorithm>

class LessThan 
{
public:
	LessThan(int val) :_val(val) {};
	int comp_val() const { return _val; }//基值的读取
	void comp_val(int nval) { _val = nval; }//基值的写入

	bool operator () (int _value) const;
private:
	int _val;
};
inline bool LessThan::operator ()(int value) const 
{
	return value < _val;
}
int count_less_than(const std::vector<int> &vec, int comp)
{
	LessThan lt(comp);
	int count = 0;
	for (int ix = 0; ix < vec.size(); ++ix)
		if (lt(vec[ix])) //这里就是使用了function object
		//使得vec[ix]小于comp即返回true
			++count;
	return count;
}

void print_less_than(const std::vector<int> &vec, int comp, std::ostream &os = std::cout)
{
	LessThan lt(comp);
	std::vector<int>::const_iterator iter = vec.begin();
	std::vector<int>::const_iterator it_end = vec.end();

	os << "elements less than" << lt.comp_val() << std::endl;
	//一种高效简洁的while的写法
	while ((iter = find_if(iter,it_end,lt))!=it_end)
	{
		os << *iter << " ";
		++iter;
	}
}

mian程序

#include "LessThan.h"
#include <iostream>

int main() 
{
	int ia[16] = { 17,12,44,9,18,45,6,14,
	34,67,9,0,27,55,8,16 };
	std::vector<int> vec(ia, ia + 16);
	int comp_val = 20;

	std::cout << "number of elements less than" << comp_val << "are"
		<< count_less_than(vec, comp_val) << std::endl;
	print_less_than(vec, comp_val);
	getchar();
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值