C++类与面向对象(侯捷)

  • Classes的两个经典分类:
    • Class without pointer member
    • Class with pointer member

class without pointer member

头文件与类的声明

头文件防卫式声明!!!!

#ifndef __COMPLEX__
#define __COMPLEX__

...


#endif

header的布局

#ifndef __COMPLEX__
#define __COMPLEX__

#include <cmath>
class ostream;
class complex;					// 前置声明

complex&
	__doap1(complex* ths, const complex& r);

class complex					// 类-声明
{
	...
};

complex::function ...			// 类-定义

#endif

class的声明

class template类模版

template<typename T>
class complex					// class head
{								// class body
public:
	complex (T r = 0, T i = 0)
        : re(r), im(i)
	{ }
	complex& operator += (const complex&);	// 有些函数在body定义,有些在body外定义
	T real () const {return re;}		// inline函数
	T imag () const {return im;}
private:
	T re, im;
	friend complex& __doapl (complex*, const complex&);

};

complex<int> c(2, 1); 

inline函数

  • 函数在class body内定义完成,成为inline候选人(是否真的变成inline由编译器决定)
  • 不在body中定义,可以加inline关键字

function template 函数模版

  • 函数模版在调用时不需要指明类型
  • 编译器对function template进行实参推导(argument deduction)
template <class T>
inline
const T& min(const T& a, const T& b){
    return b < a? b: a;		// class T 要重载操作符<
}

access level(访问级别)

public, private, protected

complex<int> c1(2, 1);
cout << c1.real();
cout << c1.imag();

数据要设为private,要用public函数读写,不能直接通过对象去拿

constructor(构造函数)

  • 函数名一定要和类名称一样
  • 可以有参数,参数可以有默认值 default argument;complex c2;
  • 不需要返回值类型
  • 程序中通过创建对象调用构造函数
  • 不带指针的类多半不用析构函数

变量的数值设定有两个阶段:初始化阶段和赋值阶段

class complex
{
	// 构造函数
	complex(double r = 0, double i = 0)
    	: re(r), im(i)	// initialization list 初始列(构造函数才有)
	{ }

	// 写法二(不要这么写!!!!)
	complex(double r = 0, double i = 0)
	{ re = r; im = i;}	// 赋值阶段(效率低)
}

ctor(构造函数)可以有多个-overloading(重载)

complex() : re(0), im(0) { }	// 不能与前一种情况同时存在,默认值冲突

void real(double r) { re = r; }
  • real函数编译后的实际名称不一样

单例模式

  • ctors可以放在private里
class A{
public:
	static A& getInstance();
	setup() {...}
private:
	A();
	A(const A& rhs);
	...
};

A& A::getInstance()
{
	static A a;	// 只有调用才会出现,也只有这一份
    return a;
}

// 调用
A::getInstance.setup();

const member functions(常量成员函数)

  • 不会改变数据内容加const关键字
{
    const complex c1(2, 1);		// c1有const修饰,则real、imag函数该加const修饰
    cout << c1.real();
    cout << c1.imag();
}

参数与返回值

参数传递

  • pass by value、pass by reference ( to const )
  • 尽量传引用

如:const comlex& 不可修改
返回值

  • 也尽量传引用

什么时候可以用pass by reference、return by reference?

  • 在函数作用域下创建的变量,不能返回引用,其他情况都能返回引用
  • 传递者无需知道接收者是以reference形式接收;

friend(友元函数)

  • 友元函数可以访问私有数据
class complex
{
private:
	friend complex& __doapl (complex*, const complex&);
}


inline complex&
__doapl (complex* ths, const complex& r)
{
    ths->re += r.re;
    ths->im += r.im;
    return *ths;	// 返回一个object
}
  • 相同class的各个object互为friends

operator overloading

⚠️注意:操作符 :: . .* ?: 四个不能重载

操作符重载-1,成员函数 this

inline complex&
__doapl(complex* ths, const complex& r)	// do assignment plus
{
    ths->re += r.re;
    ths->im += r.im;
    return *ths;
}

inline complex&
complex::operator += (const complex& r)	// 参数列隐藏了this
{
    return __doapl(this, r);
}

c2 += c1;	// 作用于c2,c2->this,c1->r
c3 += c2 += c1;
  • 任何成员函数都有一个this参数,指向调用的对象

操作符重载-2,非成员函数 无this

  • client有几种可能的用法,就要开发几种函数
inline complex
operator + (const complex& x, const complex& y)
{
    // typename();
    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));
}
  • 为什么不返回reference?

因为接收结果的变量在函数中才定义,函数结束后会销毁,返回的必定是local object

  • **typename()**创建临时对象,生命到下一行就结束

只能写成非成员函数的操作符重载的形式
(因为c1 << cout 无这种用法,所以不能写成成员函数)

#include<iostream.h>
ostream&	// cout有连串的输出,所以不能返回void
operator << (ostream& os, const complex& x)
{
    // os的状态一直在改变,所以不能const
    return os << '(' << real(x) << ','
              << imag(x) << ')';
}

⭕️ 总结整理:设计一个类

class with pointer member

Big Three(拷贝构造、拷贝赋值、析构函数)

  • Class内有指针的拷贝构造、拷贝赋值一定要自己写
#ifndef __MYSTRING__
#define __MYSTRING__

class String
{
public:
	String(const char* cstr = 0);
	// Big Three, 三个特殊函数
	// 拷贝构造:构造函数,接收自己类型
    Stirng(const String& str);
	// 拷贝赋值
    String& operator=(const String& str);
	// 析构函数
	~String();

	char* get_c_str() const { return m_data; } //inline, 不改变data,加const

private:
	char* m_data;
}

#endif
int main()
{
    String s1();
    String s2("hello");

	String s3(s1);
    cout << s3 << endl;
    s3 = s2;
    cout << s3 << endl;
}

ctor和dtor(构造函数和析构函数)

  • class内有指针,多半需要动态分配内存,则需要析构函数
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;	// 动态分配的内存需要delete,否则内存泄露    
}

浅拷贝与深拷贝

  • 浅拷贝:指针之间的拷贝,指针指向同一块内存,会造成内存泄露
  • 深拷贝:

拷贝构造

inline
String::String(const String& str)
{
    m_data = new char[ strlen(str.m_data) + 1];	// 可以直接取对象的private,兄弟之间互为friend
    strcpy(m_data, str.m_data);
}

// 以下两者等价
String s2(s1);
String s2 = s1;

拷贝赋值

inline
String& String::operator=(const String& str)
{
    // 检测自我赋值(self assignment)!!!!!
    if(this == &str)
        return *this;
    // 先删除原有的
    delete[] m_data;
    // 再动态申请空间
    m_data = new char[ strlen(str.m_data) + 1];
    // 最后进行赋值
    strcpy(m_data, str.m_data);
    return *this;
}

output函数

ostream& operator<<(ostream& os, const String& str)
{
    os << str.get_c_str();
    return os;
}

stack(栈), heap(堆)

stack

  • 存在于某作用域(scope)的一块内存空间中
  • 当调用函数,函数本身即会形成一个stack用来放置它接收的参数以及返回地址
  • 在函数本体内声明的任何变量,其所使用的内存块都取自stack

heap

  • 又称为system heap,指由操作系统提供的一块global内存空间,动态分配
  • 必须手动delete

⭕️ 总结整理:各种对象的生命周期

new,delete

  • new:先分配memory,再调用ctor
String *pc;
void* mem = operator new(sizeof(String));	// 分配内存,其内部调用malloc(n)
pc = static_cast<String*>(mem);				// 转型
pc -> String::String("Hello");				// 构造函数
  • delete:先调用dtor,再释放memory
String::~String(ps);						// 析构函数,内部有delete
operator delete(ps);						// 释放内存,其内部调用free(ps)

🌟 memory block 内存块

动态分配所得的内存块, in VC

image.png
cookie+debug header下 运行下
内存对齐,需要加pad保证16的倍数

cookie

  • 上下cookie记录整块的大小:64个byte则是40,最后一位标识内存状态:0-已收回,1-已给出
  • malloc和free需要用到cookie

动态分配所得的array

image.png

  • array new一定要搭配array delete,只用delete只会调用一次dtor
  • 如果里面没有指针,没有做动态分配,则不会造成动态泄漏

image.png

#ifndef __MYSTRING__
#define __MYSTRING__

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;
}
#endif

inline
String::String(const char* cstr = 0){
    if(ctr){
        m_data = new char[strlen(str)+1];
    	strcpy(m_data, str);
    }
    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;
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
    return *this;
}

补充

static关键字

静态数据或静态函数

  • 整个类只有一份,与对象脱离
  • 静态函数没有this pointer
class Account{
public:
	static double m_date;		// 声明
	static void set_rate(const double& x) { m_rate = x; }
};

double Account::m_rate = 8.0;	// 定义,可以不赋值
  • 调用static函数的方式
    • 通过object调用
    • 通过class name调用
Acount::set_rate(7.0);
Account a;
a.set_rate(7.0);
  • 单例模式

cout

extern __IO_ostream_withassign cout;继承自ostream

function template,函数模版

  • 编译器会做argument deduction,进行实参推导,参数推导的结果为A,则调用A::operator<
  • 与类模版的区别是不需要特定指出类型
template <class T>
inline
const T& min(const T& a, const T& b)
{
    return a < b? a : b;	// 操作符重载<
}

namespace

namespace std	// 标准库所有的东西都包含在std中
{
	...
}
  • using directive,从此不需要加std
  • using declaration, using std:: cout,一条一条声明,直接用cout
  • 全名

面向对象三大关系

Composition(复合) has-a

  • 设计模式:Adapter(queue )

image.png

template<class T, class sequence = deque<T> >
class queue{
	...
protected:
	sequence c;	// 底层容器 deque<T> c;
public:
	// 完全利用c的操作函数完成
	bool empty() const { return c.empty(); }
	size_type size() const { return c.size(); }
	reference front() const { return c.front(); }
	reference back() const { return c.back(); }

	void push(const value_type& x) { c.push_back(x); }
	void pop() { c.pop_front(); }
}

composition关系下的构造和析构

  • 构造由内而外

先调用Component的default构造函数

Container::Container(...): {...};		
// 可以自己指定构造函数
Container::Container(...): Component() {...};
  • 析构由外而内
Container::~Container(...) {}
Container::~Container(...) {... ~Componet(); } //编译器

Delegation(委托)Composition by reference

image.png

  • 与composition的区别
    • Composition是有外部就一定有内部
    • Delegation不同步,等到需要的时候才创建

🌟 pimpl : pointer to implementation

称pimpl,或 Handle/Body 或 编译防火墙

class StringRep;
class String{
public:
	...
private:
	StringRep* rep;	// pimpl
}
#include "String.hpp"
namespace{
class StringRep{
friend class String;
	StringRep (const char* s);
	~StringRep();
	int count;
	char* rep;
};
}

image.png

a,b,c共享同一块内存,但是copy on write
shared_ptr

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;
};

image.png

  • base class的dtor必须是virtual,否则会出现undefined behavior

🌟虚函数

  • 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;
	...
};

class Rectangle: public Shape {...};
class Ellipse: public Shape {...};
  • 子类对象调用父类的函数
Template Method 设计模式
class CDocument{
public:
	OnFileOpen();
	// this->Serialize(); (*(this->vptr)[n])(this);
	virtual void Serialize() { };
};

class CMyDoc : public CDocument{
public:
	virtual void Serialze() {...};
};

main(){
    CMyDoc myDoc;
    ...
    // CDocument::OnFileOpen(&myDoc);
    myDoc.OnFileOpen();
}

Inheritance + Composition关系下的构造和析构

image.png

输出构造与析构顺序如下:

Base's ctor completed
Component's ctor completed
Derived's ctor completed
Derived's dtor completed
Component's dtor completed
Base's dtor completed

Component's ctor completed
Base's ctor completed
Derived's ctor completed
Derived's dtor completed
Base's dtor completed
Component's dtor completed

Delegation+ Inheritance

Observe设计模式

image.png

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);
    }
};

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


Composite设计模式

文件系统
image.png

class Component
{
	int value;
public:
	Component(int val) { value = val; }
	virtual void add(Component*) { };
};

class Primitive: public Component
{
public:
	Primitive(int val): Component(val) {}
};

class Composite
{
	vector<Component*> c;
public:
	Composite(int val): Component(val) {}
	void add(Component* elem){
        c.push_back(elem);
    }
};

Prototype 原型设计模式

image.png

#include<iostream.h>
enum imageType
{
	LSAT, SPOT
};

class Image
{
public:
	virtual void draw() = 0;
	virtual Image* clone() = 0;
	static Image* findAndClone(imageType);

protected:
	virtual imageType returnType() = 0;
	virtual Image* clone() = 0;
	static void addPrototype(Image* image){
        _prototypes[_nextSlot++] = image;
    }

private:
	static Image* _prototype[10];
	static int _nextSlot;
};

Image *Image::_prototype[];
int Image::_nextSlot;

Image *Image::findAndClone(imageType type){
    for(int i=0;i<_nextSlot;i++){
        if(_prototype[i]->returnType() == type){
            return _prototype[i]->clone();
        }
    }
}


// 子类
class LandSatImage: public Image
{
public:
	imageType returnType(){
        return LSAT;
    }
    void draw(){
        
    }
	Image *clone(){
        return new LandSatImage(1);
    }

protected:
	LandSatImage(int dummy){
        _id = _count++;
    }
private:
	static LandSatImage _landSatImage;
	LandSatImage(){
        addPrototype(this);
    }
	int _id;
	static int _count;
};

LandSatImage LandSatImage::_landSatImage;
int LandSatImage::_count = 1;

⭕️ 总结整理:设计模式

Reference

C++📚

  1. C++ primer、The C++ programming language
  2. Effective C++(规范)
  3. The C++ standard library、stl源码剖析

更多细节
image.png

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yyy_jin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值