C++面向对象程序设计基础篇

Object Based 面对单一class设计

> 经典分类

Class without pointer member(s)

  • complex
    Class with pointer member(s)
  • string

> complex例子

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

#ifndef __COMPLEX__
#define __COMPLEX__

...

#endif

避免重复包含头文件,导致重复定义。

> complex class需求

{
    complex c1(2,1);
    complex c2;
    cout << c1 << endl;
    cout << c2 << endl;
    
    c2 = c1 + 5;
    c2 = 6 + c1;
    c2 = c1 + c2;
    c2 += c1;
    c2 += 3;
    c2 = -c1;
    
    cout << (c1 == c2) << endl;
    cout << (c1 != c2) << endl;
    cout << conj(c1) << endl;
}

> complex class设计

class complex{
	public:
		complex (double r=0, double i=0)
			: re(r), im(i)
		{ }
	complex& operator += (const complex&);
	double real () const { return re; }	//inline
	double imag () const { return im; }	//inline
	private:
		double re, im;

	friend complex& __doapl (complex*, const complex&);
};

inline double
imag(const complex& x)
{
	return x.imag();
}

> 构造函数重载ctor-overloading

complex c1;
complex c2();	//等价,等同于调用无参构造函数

若遇到如下情况,编译器将无能为力。

complex (double r=0, double i=0) : re(r), im(i) {}
complex () : re(0), im(0) {} 

两者都可以接受无参构造。

> 常量成员函数 const member functions

{
    complex c1(2,1);
    cout << c1.real();
    cout << c1.imag();
}

{
    const complex c2(2,1);
    cout << c2.real();
    cout << c2.imag();
}

若在real和imag函数中,没有声明为const类型,左边例子正确执行。但在右例中,由于c2是const对象,而调用了非常量成员函数,进而有修改成员变量的风险。

> 参数传递 pass by value vs. pass by reference(to const)

地址传递比值传递效率更高,但有副作用,可能引起值的更改。
const限制参数是否能修改。

complex & complex::operator += (const complex&);
ostream & operator << (ostream & os, const complex &x);

操作符+=、<<都使用对象,而不修改对象,为了效率又限制修改对象,使用const complex &。

> 返回值传递 return by value vs. Return by reference(to const)

按照C++运算规则,以下操作合法。

a += b += c;
cout << a << b << c << endl;

运算符+=和<<在C++均可被重载,由程序员指定具体行为。
这样的运算符为左结合,为了b.operator +=()作用后,a仍然能处理。
a.operator += ( b.operator += ( c ) );
同理,cout处理a后,需要仍然能处理b、c。所以两者的都应返回对象的引用。

ostream & operator<< (ostream os, const complex& x)
{
	return os << ‘(’ << x.real() << ‘,’ << x.imag() << ‘)’;
}

> 友元 friend

申明为友元的函数,可以任意访问该类的私有成员变量。在一定程度上破坏了类的封装性,但也使得存取变量效率更高。
相同class的各个objects互为友元。
例:

class complex
{
	public:
		int func(const complex& param){ return param.re + param.im; }//访问私有成员
	private:
		double re, im; 
};
complex c1;
complex c2;
c2.func(c1);

> 成员函数 vs. 全局函数

为使得以下成立,可以均使用全局函数。

{
    complex c1;
    complex c2(1,2);
    c2 = c1 + c2;
    c2 = c1 + 5;
    c2 = 7 + c1;
}
inline complex & operator+ (const complex& x1, const complex& x2); 
inline complex operator+ (const complex& x1, doublex2); 
inline complex operator+ (double x1, const complex& x2); 

c2 = c1+c2将c1加c2运算结果放在c2中,c2空间本来存在,可以返回引用更快。
然而,c1+5和7+c1运算的结果在operator+中存储在临时空间里,退出作用域后,栈上临时空间自动销毁,故不能返回引用。

每个函数,都有可能设计为两种情况,成员函数和全局函数。但在7+c1例中,使用全局函数更好(也可考虑使用类型转换构造函数)。

> 临时对象 temp objects

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

使用typename ()的方法创建临时对象,它们无名,生命周期仅限在当前语句。

> 另外的操作符重载

inline complex operator + (const complex& x);	//正
inline complex operator - (const complex& x);	//负

正操作符中,无需更改,可以直接返回x的引用,也可返回临时对象。
负操作符中,一定不能返回x的引用,因为是local object。

inline complex operator - (const complex& x)
{
	return complex (-real(x), -imag(x)); 
}

此外,需要重载==、!=等操作符,以供复数使用。

> String例子

> 三个特殊函数 Big Three

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

构造函数、析构函数和赋值运算统称为big three。
在不自己编写赋值运算时,编译器将使用默认按位拷贝。在动态分配内存例子中,会引起内存泄漏。
因此在含有指针的类中,应该使用深拷贝,另外开辟空间,并把内容拷贝,而不是仅拷贝指针。还应注意检查不要自我赋值。

> 栈堆

Stack,是存在某个作用域的一个内存空间。在函数体内声明的任何临时变量,都位于栈中。Stack objects的生命周期在作用域结束之后结束,将被自动清理。Static local objects的生命周期和整个程序一致。Global objects也可视为一种static object,作用域为整个程序。
Heap,由操作系统提供的一块全局内存空间,程序可动态分配其中的若干块。
Heap objects的生命周期直到调用delete为止。

> new

先分配内存,再调用构造函数。

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

编译器转化为:

complex *pc;
void *mem = operator new( sizeof(complex) );	//内部调用malloc
pc = static_cast<complex*> (mem);			//转型
pc->complex::complex(1,2);					//构造函数

> delete

与new相对应,先调用析构函数,再释放内存。

delete pc;

编译器转化为:

complex::~complex(pc);		//析构函数
operator delete(pc);		//释放内存,内部调用free

> array new与array delete
在这里插入图片描述
在VC中,动态分配内存,首尾四字节存储分配内存大小,4bits对齐(16整除)。最后一比特为1,为以分配;为0,为未分配状态。Debug模式将会多占36bytes。以数组动态分配还会在实际分配空间之上多分配4bytes存储数组大小。

delete [] p;		//唤起3次dtor
delete p;			//唤起1次dtor

如果没有配套使用array new和array delete,则会引起m_data指向区域内存泄漏,而p指向对象区域是可以正常回收的。

> static

类中存在静态成员变量和非静态成员变量,非静态成员变量在每个类对象中均有一份,而静态成员变量所有类对象共用。
成员函数,无论是否静态,都只有一份。唯一不同点在于静态成员函数,仅能操作静态成员变量。
由于静态成员变量仅有一份,不需要this来指定具体属于哪一个类对象。所以在访问时,可以无需创建对象。

class Account{
	public:
		static double m_rate;
		static void set_rate(const double& x) { m_rate = x; }
};
double Account::m_rate = 8.0;		//定义,分配空间
Account::set_rate(5.0);

> this指针

在非静态成员函数中,会悄悄传递一个this作为函数参数,用来指定调用哪一个类对象。
而静态成员函数,由于是共用的,函数里不再有this指针。

> Singleton & Meyers singleton

singleton将构造函数放在private中

class A{
	public:
		static A& getInstance() { return a; }
		setup() { ... }
	private:
		A();
		A(const A& rhs);
		static A a;
	...
}; 

编写构造函数,编译器将不会自动加入默认构造函数。
构造函数设为私有,外界无法调用,创建新的对象。
在私有成员中,A仅有一份,通过唯一接口getInstance访问。
缺点是A总是存在。

class A{
	public:
		static A& getInstance();
		setup() { ... }
	private:
		A();
		A(const A& rhs);
		...
}; 
A& A::getInstance()
{
	static A a;
	return a;
}

这样在调用getInstance时,才创建A。

A::getInstance().setup();

> 类模板 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;
};
{
	complex<double> c1(2.5, 1.5);
	complex<int> c2(2,6);
}

> 函数模板 function template

函数在调用时,才能确定参数或返回值类型。

template <class T>
inline
const T& min(const T& a, const T& b)
{
	return b < a ? b : a;
}

在调用函数模板时,会进行参数推导。

示例代码

/* complex.h */
#ifndef __COMPLEX__
#define __COMPLEX__

#include <iostream>
using namespace std;

class complex{
    private:
        double re, im;
    public:
        complex(double r=0,double i=0)
            : re(r), im(i)
        { }
        complex& operator += (const complex& x);
        complex& operator += (double x);
        double real() const { return re; }
        double imag() const { return im; }

        friend ostream& operator << (ostream& os, const complex& x);
        friend complex& __doapl(complex* x, const complex& y);
};

inline ostream&
operator << (ostream& os, const complex& x)
{
    return os << "(" << x.real()
        << "," << x.imag() << ")";
}

inline complex&
complex::operator += (const complex& x)
{
    return __doapl(this, x);
}

inline complex&
complex::operator += (double x)
{
    complex tmp(x, 0);
    return __doapl(this, tmp);
}

inline complex&
__doapl(complex* x, const complex& y)
{
    x->im += y.im;
    x->re += y.re;
    return *x;
}

inline complex
operator + (const complex& x, const complex& y)
{
    return complex(x.real() + y.real(),
                   x.imag() + y.imag());
}

inline complex
operator + (const complex &x, double y)
{
    return complex(x.real() + y, x.imag());
}

inline complex
operator + (double x, const complex& y)
{
    return complex(x + y.real(), y.imag());
}

inline complex
operator + (const complex& x)
{
    return x;
}

inline complex
operator - (const complex& x)
{
    return complex(-x.real(), -x.imag());
}

inline bool
operator == (const complex& x, const complex& y)
{
    return (x.real() == y.real())
        && (x.imag() == y.imag());
}

inline bool
operator == (const complex& x, double y)
{
    return x.imag() == 0 && x.real() == y;
}

inline bool
operator == (double x, const complex& y)
{
    return y.imag() == 0 && y.real() == x;
}

inline bool
operator != (const complex& x, const complex& y)
{
    return (x.real() != y.real())
        || (x.imag() != y.imag());
}

inline bool
operator != (const complex& x, double y)
{
    return x.imag() != 0 || x.real() != y;
}

inline bool
operator != (double x, const complex& y)
{
    return y.imag() != 0 || y.real() != x;
}

inline complex
conj(const complex& x)
{
    return complex(x.real(), -x.imag());
}

#endif

/* complex-test.cpp */

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

using namespace std;

int main()
{
    complex c1(2,1);
    complex c2;
    cout << c1 << endl;
    cout << c2 << endl;

    c2 = c1 + 5;
    c2 = 7 + c1;
    c2 = c1 + c2;
    c2 += c1;
    c2 += 3;
    c2 = -c1;

    cout << (c1 == c2) << endl;
    cout << (c1 != c2) << endl;
    cout << conj(c1) << endl;

    return 0;
}

/* string-test.cpp */

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

using namespace std;

int main()
{
    String str1;
    String str2("Hello");
    String *str3 = new String("World");

    str1 = "Hello, world!";

    cout << str1.get_c_str() << endl;
    cout << str2.get_c_str();
    cout << str3->get_c_str() << endl;

    return 0;
}

/* string.h */

#ifndef __STRING_H__
#define __STRING_H__

#include <iostream>
#include <string.h>

using namespace std;

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

inline
String::String(const char *cstr)
{
    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(const String& s)
{
    m_data = new char[strlen(s.m_data)+1];
    strcpy(m_data, s.m_data);
}

inline String&
String::operator = (const String& s)
{
    if(this == &s)
        return *this;

    delete [] m_data;
    m_data = new char[strlen(s.m_data)+1];
    strcpy(m_data, s.m_data);
    return *this;
}

inline
String::~String()
{
    delete [] m_data;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值