当前位置: 首页 > news >正文

C++面向对象高级编程(上)

C++面向对象高级编程(上)

C++面向对象高级编程(下)

C++ 正规规范编码

(学C++就是为了效率,要注意小的习惯,一出手就不同凡响!)

1、Header(头文件)中的防卫式声明

complex.h

#ifndef _COMPLEX_
#define _COMPLEX_

...

#endif

2、如果 成员函数 只读 数据(成员变量),而不去修改,则该 成员函数 后面要加 const

const object(data members 不得修改)non-const object(data members 可修改)
const member functions (保证不更改data members)
non-const member functions (不保证data members不变)×
const String str("hello C++");
str.print();
//如果当初设计String::print() 时未指明 const 
//那么上行便是经由const object 调用 non-const object member function,会出错。

除了上述规则,当成员函数的constnon-const 版本同时存在时:

  • const object只会(只能)调用 const版本,
  • non-const object只会(只能)调用 non-const版本。

calss template std::basic_string<…>有如下两个 member functions:

charT
operator[](size_type pos)const
{.../*不必考虑Copy On Write, 常量对象一定不会改内容!*/ }

reference
operator[](size_type pos)
{.../*必须考虑Copy On Write*/ }

const 算函数签名的一部分!

3、参数传递 尽量传引用 &(速度更快),但是传过去又不想被改,就加 const返回类型尽量 返回引用 &

4、相同class 的各个objects 互为 friends (友元)

class complex{
public :
	complex (double r = 0 , double i = 0): re (r), im (i)
	{ }
	
	//complex() : re(0), im(0) {} //错误,和第一个冲突了
	
	double real ()const { return re; }//不希望改变,函数就加const
	double imag ()const { return im; }

	int func (const complex& param)//互为 friends 
	{return param.re + param.im;}
	
private:
	double re, im;
} ;

{
	complex c1(2, 1);
	complex c2;
	
	c2.func(c1) ;
}

5、以下不能返回引用函数体内创建的对象,离开函数就死亡了!

inline complex operator+ (const complex& x, const complex& y)
{
	return complex(real(x) + real(y), imag(x) + imag(y));//类型名 + 小括号 (创建临时对象)
}
inline complex operator+ (const complex& x, double y)
{
	return complex(real(x) + y, imag(x));
}
inline complex operator + (double x, const complex& y)
{
	return complex(x + real(y), imag(y));
}

6、如果拷贝带指针,一定不要用编译器默认版本,要自己写!(防止浅拷贝)

class String
{
public:
	String(const char* cstr = 0);
	String(const String& str);
	String& operator=(const String& str);
	~String();
	char* get_c_str() const { return m_data; }
private:
	char* m_data;//指针
};

inline String::String(const char* cstr = 0)
{
	if (cstr) {
		m_data = new char[strlen(cstr)+1];
		strcpy(m_data, cstr);
	} else {// 未指定初值 
		m_data = new char[1];
		*m_data = '\0';
	}
}
//析构
inline String::~String()
{
	delete[] m_data;//一定要杀掉删除
}

//拷贝构造
inline String::String(const String& str)
{
	m_data = new char[ strlen(str.m_data) + 1 ];//深拷贝
	strcpy(m_data, str.m_data);
}

//拷贝赋值
inline String& String::operator=(const String& str)
{
	if (this == &str)//一定要检测自我赋值,可能指向同一个空间
		return *this;
		
	delete[] m_data;//搭配 array new
	
	m_data = new char[ strlen(str.m_data) + 1 ];
	strcpy(m_data, str.m_data);
	
	return *this;
}

7、(stack)栈、(heap)堆与内存管理

Stack 是存在于某作用域(scope)的一块内存空间(memory space)。(离开作用域,系统会自动清理!)

  • 例如当你调用函数,函数本身即会形成一个stack用来放置它所接受的参数,以及返回地址;
  • 在函数本体(function body)内声明的任何变量,其所使用的内存块都取自上述的stack。

Heap, 或谓 system heap, 是指由操作系统提供的一块 global内存空间,程序可动态分配(dynamic allocated),使用new创建,从某中获得若干区块(blocks)。(此开辟的空间使用完,需要我们手动释放 delete,不然会内存泄漏!)

8、new : 先分配 memory , 再调用构造函数

Complex* pc = new Complex(1,2);

编译器转化为:

Complex *pc;

void* mem = operator new( sizeof(Complex) ); //第一步、分配內存(new 其内部调用malloc(n))

pc = static_cast<Complex*>(mem); //第二步、转型

pc->Complex::Complex(1,2); //第三步、构造函数

8、delete : 先调用析构函数,再释放 memory

Complex* pc = new Complex(1,2);
...
delete pc;

编译器转化为:

Complex::~Complex(pc); // 第一步、析构函數
operator delete(pc); //第二步、释放內存 (delete  其内部调用 free(pc))

9、静态变量 要用 静态函数 调用。

class Account {
public:
	static double m_rate; //只是声明,静态变量
	static void set_rate(const double& x) { m_rate = x; }//静态函数
};

double Account::m_rate = 8.0; //赋初值

int main() {//调用static函数的方式有两种:
	Account::set_rate(5.0);//1、通过class name调用
	Account a;
	a.set_rate(7.0);//2、通过object调用
}

推荐写法:

class A {
public:
	static A& getInstance();
	setup() { ... }
private:
	A();
	A(const A& rhs);
	...
};
A& A::getInstance()//只有别人调用这个函数时,才会生成静态变量a,且只有一份
{
	static A a;
	return a;
}

10、class template, 类模板

template<typename T>
class complex
{ 
public:
	complex (T r = 0, T i = 0): re (r), im (i)
	{ }
	complex& operator += (const complex&);
	T real () const { return re; }
	T imag () const { return im; }
private:
	T re, im;
	
	friend complex& __doapl (complex*, const complex&);
};
//使用
{
	complex<double> c1(2.5, 1.5);
	complex<int> c2(2, 6);
	...
}

11、namespace

namespace std
{
	...
}
#include <iostream.h>
using namespace std;

int main()
{
	cin << ...;
	cout << ...;
	return 0;
}

12、Object Oriented Programming, Object Oriented Design (OOP, OOD)

面向对象类和类的关系:

  • Inheritance (继承)
  • Composition (复合)
  • Delegation (委托)

1)、Composition (复合),表示: has-a

  • queue 拥有 deque,左边(Container) 拥有右边(Component)

在这里插入图片描述

  • 构造由内而外Container的构造函数首先调用Componentdefault构造函数,然后才执行自己。(红色部分编译器自动加的!)

在这里插入图片描述

  • 析构由外而内Container的析构函数首先执行自己,然后才调用Component的析构函数。

在这里插入图片描述

下面的设计模式为:Adapter

template <class T, class Sequence = deque<T>>
class queue {//队列
...
protected :
	Sequence c;     //底层容器
public:
	// 以下完全利用 c 的操作函数完成
	bool empty() const { return c.empty(); }
	size_type size() const { return c.size(); }
	reference front() { return c.front(); }
	reference back() { return c.back(); }
	//
	void push(const value_type& x) { c.push_back(x); }
	void pop() { c.pop_front(); }
}

等价于:

template <class T>
class queue {
...
protected:
deque<T> c; // 底層容器
public:}

2)、Delegation (委托)Composition by reference.(编译防火墙)

左边(Handle)(拥有指针)指向右边(Body),不是同时存在的,只有左边用到右边时,右边才存在。
在这里插入图片描述

左边(Handle)

// file String.hpp
class StringRep;
class String {
public:
	String();
	String(const char* s);
	String(const String& s);
	String &operator=(const String& s);
	~String();
. . . .
private:
	StringRep* rep; // pimpl,指向右边的指针
};

右边(Body)

// file String.cpp
#include "String.hpp"
namespace {

class StringRep {
friend class String;
	StringRep(const char* s);
	~StringRep();
	int count;
	char* rep;
};

}
String::String(){ ... }
...

3)、Inheritance (继承),表示is-a

  • 继承最有价值的地方是和虚函数打通
struct _List_node_base//父类
{
	_List_node_base* _M_next;
	_List_node_base* _M_prev;
};

template<typename _Tp>
struct _List_node : public _List_node_base //子类
{
	_Tp _M_data;
};

base class 的 dtor,必须是 virtual,否则会出现undefined behavior
在这里插入图片描述

  • 构造由内而外Deriverd(子类)的构造函数首先调用Base(父类)的default构造函数,然后才执行自己。(红色部分编译器自动加的!)

在这里插入图片描述

  • 析构由外而内Deriverd(子类)的析构函数首先执行自己,然后才调用Base(父类)的析构函数。

在这里插入图片描述

13、Inheritance (继承) with virtual functions(虚函数)(成员变量继承占用内存,函数继承的是 调用权

  • non-virtual 函数:你不希望derived class 重新定义(override, 覆写) 它.
  • virtual 函数:你希望derived class 重新定义(override, 覆写) 它,且你对它已有默认定义
  • pure virtual 函数:你希望derived class 一定要重新定义(override 覆写)它,你对它没有默认定义
class Shape {
public:
	virtual void draw( ) const = 0;//pure virtual
	virtual void error(const std::string& msg);//impure virtual
	int objectID( ) const;//non-virtual
	...
};

class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };

在这里插入图片描述
14、Inheritance(继承)+Composition(复合)关系下的构造和析构

在这里插入图片描述

  • 构造由内而外Deriverd(子类)的构造函数首先调用Base(父类)的default构造函数,然后调用Componentdefault构造函数,然后才执行自己。(红色部分编译器自动加的!)

在这里插入图片描述

  • 析构由外而内Deriverd(子类)的析构函数首先执行自己,然后调用Component的析构函数,然后才调用Base(父类)的析构函数

在这里插入图片描述

14、Delegation(委托) + Inheritance(继承)关系下的构造和析构

在这里插入图片描述

Subject

class Subject
{
	int m_value;
	vector<Observer*> m_views;
public:
	void attach(Observer* obs)
	{
		m_views.push_back(obs);
	}
	void set_val(int value)
	{
		m_value = value;
		notify();
	}
	void notify()
	{
		for (int i = 0; i < m_views.size(); ++i)
		m_views[i]->update(this, m_value);
	}
};

Observer(将来可能被继承)

class Observer
{
public:
	virtual void update(Subject* sub, int value) = 0;
};

在这里插入图片描述
Prototype

Image

#include <iostream.h>
enum imageType
{
	LSAT, SPOT
};
class Image
{
public:
	virtual void draw() = 0;
	static Image *findAndClone(imageType);
	
protected:
	virtual imageType returnType() = 0;
	virtual Image *clone() = 0;
	// As each subclass of Image is declared, it registers its prototype
	static void addPrototype(Image *image)
	{
		_prototypes[_nextSlot++] = image;
	}
	
private:
	// addPrototype() saves each registered prototype here
	static Image *_prototypes[10];
	static int _nextSlot;
};
Image *Image::_prototypes[];
int Image::_nextSlot;

// Client calls this public static member function when it needs an instance
// of an Image subclass
Image *Image::findAndClone(imageType type)
{
	for (int i = 0; i < _nextSlot; i++)
		if (_prototypes[i]->returnType() == type)
			return _prototypes[i]->clone();
}

LandSatImage

class LandSatImage: public Image
{
public:
	imageType returnType() {
		return LSAT;
	}
	void draw() {
		cout << "LandSatImage::draw " << _id << endl;
	}
	// When clone() is called, call the one-argument ctor with a dummy arg
	Image *clone() {
		return new LandSatImage(1);
	}
	
protected:
	// This is only called from clone()
	LandSatImage(int dummy) {
		_id = _count++;
	}
	
private:
	// Mechanism for initializing an Image subclass - this causes the
	// default ctor to be called, which registers the subclass's prototype
	static LandSatImage _landSatImage;
	// This is only called when the private static data member is inited
	LandSatImage() {
		addPrototype(this);
	}
	// Nominal "state" per instance mechanism
	int _id;
	static int _count;
};

// Register the subclass's prototype
LandSatImage LandSatImage::_landSatImage;
// Initialize the "state" per instance mechanism
int LandSatImage::_count = 1;

SpotImage

class SpotImage: public Image
{
public:
	imageType returnType() {
		return SPOT;
	}
	void draw() {
		cout << "SpotImage::draw " << _id << endl;
	}
	mage *clone() {
		return new SpotImage(1);
	}
	
protected:
	SpotImage(int dummy) {
		_id = _count++;
	}
	
private:
	SpotImage() {
		addPrototype(this);
	} 
	static SpotImage _spotImage;
	int _id;
	static int _count;
};

SpotImage SpotImage::_spotImage;
int SpotImage::_count = 1;

// Simulated stream of creation requests
const int NUM_IMAGES = 8;
imageType input[NUM_IMAGES] =
{
	LSAT, LSAT, LSAT, SPOT, LSAT, SPOT, SPOT, LSAT
};

main

int main()
{
	Image *images[NUM_IMAGES];
	// Given an image type, find the right prototype, and return a clone
	for (int i = 0; i < NUM_IMAGES; i++)
		images[i] = Image::findAndClone(input[i]);
	// Demonstrate that correct image objects have been cloned
	for (i = 0; i < NUM_IMAGES; i++)
		images[i]->draw();
	// Free the dynamic memory
	for (i = 0; i < NUM_IMAGES; i++)
		delete images[i];
}

注:个人学习笔记,仅供学习参考, 如有不足,欢迎指正!

相关文章:

  • 安装和配置 Flask
  • Java --- 继承
  • Rabbitmq了解
  • 51单片机-LED篇
  • ThreeJS-3D引擎渲染从入门到入土 搞定前端前沿技术
  • chatGPT 会给程序员带来失业潮吗?
  • 2023美赛春季赛F题思路数据代码论文分享
  • 快速搭建python爬虫管理平台
  • 从 JDK 9 到 19,认识一个新的 Java 形态(内存篇)
  • Dell CentOS 环境下安装远程管理命令racadm
  • Java设计模式-4、适配器模式
  • VUE3项目实现动态路由demo
  • 训练自己的GPT2-Chinese模型
  • 互联网公司吐槽养不起程序员,IT岗位的工资真是虚高有泡沫了?
  • 七、STM32 时钟系统
  • 网络传输文件的问题
  • Angular6错误 Service: No provider for Renderer2
  • dva中组件的懒加载
  • Laravel核心解读--Facades
  • leetcode386. Lexicographical Numbers
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Node + FFmpeg 实现Canvas动画导出视频
  • PAT A1017 优先队列
  • PAT A1092
  • Selenium实战教程系列(二)---元素定位
  • spring boot 整合mybatis 无法输出sql的问题
  • 前端技术周刊 2019-01-14:客户端存储
  • 如何设计一个比特币钱包服务
  • 微服务核心架构梳理
  • 微信开源mars源码分析1—上层samples分析
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • ${factoryList }后面有空格不影响
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (安卓)跳转应用市场APP详情页的方式
  • (论文阅读40-45)图像描述1
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (转) RFS+AutoItLibrary测试web对话框
  • (转)程序员技术练级攻略
  • (转)负载均衡,回话保持,cookie
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • .htaccess 强制https 单独排除某个目录
  • .Net 转战 Android 4.4 日常笔记(4)--按钮事件和国际化
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)...
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .net专家(张羿专栏)
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • ::前边啥也没有
  • [20181219]script使用小技巧.txt
  • [3D基础]理解计算机3D图形学中的坐标系变换
  • [ActionScript][AS3]小小笔记
  • [AIGC] MySQL存储引擎详解
  • [C++]unordered系列关联式容器