C++面向对象编程(1)

面向对象先来看看class怎么写。侯捷老师分享的如下两种class分类我觉得很到位:
•Class without pointer member(s)
•Class with pointer member(s)

1. Class without pointer member(s)

这种Class在上一篇《C++编程习惯(C++预备篇)》
中的MyComplex已经介绍了很多注意点。只有友元函数还没有举例说明。

"友元"解决什么问题:
私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦。C++ 是从结构化的C语言发展而来的,需要照顾结构化设计程序员的习惯,所以在对私有成员可访问范围的问题上不可限制太死。

C++ 设计者认为,如果有的程序员真的非常怕麻烦,就是想类的成员函数外部直接访问对象的私有成员,那还是做一点妥协以满足他们的愿望为好,这也算是眼前利益和长远利益的折中。因此,C++就有了友元(friend)的概念。

1.1 友元函数

友元函数可以访问类的私有成员。
如下两个例子分别介绍了友元成员函数和友元非成员函数。

// rectangle.hpp
#ifndef __MYRECT__
#define __MYRECT__

class CRectangle
{
public:
    CRectangle(int height = 0, int width = 0):mHeight(height), mWidth(width) 
    {}

    int getHeight();
    int getWidth();

    friend int calculateAreaFriendly(CRectangle& friendRec);// 声明友元函数

private:
    int mHeight;
    int mWidth;
};

#endif
// rectangle.cpp
#include "rectangle.hpp"

int CRectangle::getHeight()
{
    return mHeight;
}

int CRectangle::getWidth()
{
    return mWidth;
}
// main.cpp
#include "rectangle.hpp"
#include <iostream>

using namespace std;

// 非友元函数
int CalculateRectangleArea(CRectangle& rect)
{
    return rect.getHeight() * rect.getWidth();  // 非友元函数只能通过类的成员函数去访问私有成员 
}

// 友元函数 (非成员函数)
int calculateAreaFriendly(CRectangle& friendRec)
{
    return friendRec.mWidth * friendRec.mHeight; // 友元函数可以直接去访问私有成员
}

int main()
{
    CRectangle rt(100, 100);
    cout << "result = " << CalculateRectangleArea(rt) << endl;

    CRectangle rt_friend(1000, 1000);
    cout << "result = " << CalculateRectangleArea(rt_friend) << endl;
}
Gen-2:~/Documents/Projects/C++/OopDemo/frienddemo$ g++ main.cpp src/rectangle.cpp  -Iinclude -o main
Gen-2:~/Documents/Projects/C++/OopDemo/frienddemo$ ./main 
result = 10000
result = 1000000
#include <iostream>

using namespace std;

class CItem; // 前向声明。也会被用作解决a头文件相互依赖
class CList
{
public:
    void print(CItem& citem);
};

class CItem
{
    friend void CList::print(CItem& citem); // 友元函数 (成员函数)
public:
    CItem(int i):index(i)
    {}

private:
    int index;
    inline void printDetails()
    {
        cout << "CItem printDetails" << endl;
    } 
};

void CList::print(CItem& citem)
{
    cout << "CItem index = " << citem.index << endl;
    citem.printDetails();
}

int main()
{
    CItem citem(8);
    CList clist;
    clist.print(citem);   
}
CItem index = 8
CItem printDetails

1.2 友元类

对于类的私有方法,只有在该类中允许访问,其他类是不能访问的,但在开发程序时,如果两个类的耦合度比较紧密,能够在一个类中访问另一个类的私有成员会带来很大的方便。

#include <iostream>

using namespace std;

class CItem;
class CList
{
public:
    void Print(CItem& citem);
};

class CItem
{
    friend class CList; // 定义CList为CItem的友元类
public:
    CItem(int i):index_(i)
    {
        cout << " CItem construct, index_ = " << index_ << endl;
    }

private:
    int index_;
    inline void PrintDetails()
    {
        cout << "CItem printDetails" << endl;
    } 
};

void CList::Print(CItem& citem)
{
    citem.PrintDetails(); // CList作为CItem的友元类,可以访问CItem的私有函数
}

int main()
{
    CItem citem(20);
    CList clist;
    clist.Print(citem);
    
    return 0;
}
chris@chris-virtual-machine:~/Documents/Projects/C++/OopDemo1$ ./main 
CItem construct, index_ = 20
CItem printDetails

2. Class with pointer member(s)

与Class without pointer member(s)最大的区别在于,这种Class是一定要自己写 拷贝构造函数和拷贝赋值函数的(深拷贝),不能使用编译器默认的拷贝构造和拷贝赋值。当然如果涉及到new内存,可能还需要自己写析构函数。

// MyString.h
#ifndef __MYSTRING__
#define __MYSTRING__

#include <iostream>

class MyString
{
public:
    // 拷贝构造
	MyString(const char* cstr = 0);
	MyString(const MyString& str);
	// 拷贝赋值
	MyString& operator = (const MyString& str);
	// 析构函数
	~MyString();


private:
	char* m_data;
};

#endif
// MyString.cpp
#include <iostream>
#include "MyString.h"

using namespace std;

// 拷贝构造
inline MyString::MyString(const char* cstr = 0)
{
	cout << "拷贝构造MyString(const char* cstr = 0)" << endl;
	if (cstr)
	{
		m_data = new char[strlen(cstr)+1];
		strcpy(m_data, cstr);
	} 
	else
	{ // 未指定初值
		m_data = new char[1];
		*m_data = '\0';
	}
}
inline MyString::MyString(const MyString& str)
{
	cout << "拷贝构造MyString(const MyString& str)" << endl;
	// 在C++里对象没有空不空,
	// 只有指针有空不空的概念(例如上一个构造函数),指针用NULL来判断
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
}

// 拷贝赋值
inline MyString& MyString::operator = (const MyString& str)
{
	cout << "拷贝赋值MyString(const MyString& str)" << endl;
	// 检查自我赋值,即检查赋值号左右两边是不是相同的。作用如下:
	// (1) 提升效率:如果赋值符号两边是同一个,则不需要做后面的操作了; 
	// (2) 避免产生不确定的行为出错:如果赋值符号两边是同一个且这里不直接return的话,接下来会去delete[] mdata,这时候由于赋值号右边和左边是同一块地址,即相当于也delete了str的m_data,紧接着去访问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;
}
// 析构函数
inline MyString::~MyString()
{
	cout << "析构函数" << endl;
}
int main()
{
	MyString str1("hello world");// 拷贝构造MyString(const char* cstr = 0)
	MyString str2 = str1;// 【注意这里是拷贝构造】拷贝构造MyString(const MyString& str) 
	MyString str3(str1);// 拷贝构造MyString(const MyString& str)
	
	MyString str0; // 拷贝构造MyString(const char* cstr = 0)
	str0 = str1;// 【注意这里是拷贝赋值】拷贝赋值MyString(const MyString& str)
	
    return 0;
}

2.1 拷贝构造

如下三种情况下会调⽤拷⻉构造函数

  1. ⼀个对象以值传递的⽅式传⼊函数体,需要拷⻉构造函数创建⼀个临时对象压⼊到栈空间中。
void ConFun(const MyString& t1)
{
    MyString t2 = t1;// // 这里会调用拷调用拷贝构造MyString(const MyString& str)
}
  1. ⼀个对象以值传递的⽅式从函数返回,需要执⾏拷⻉构造函数创建⼀个临时对象作为返回值。
MyString ConFun()
{
   MyString a;
   return a;
}
MyString B = ConFun();// 这里会调用拷调用拷贝构造MyString(const MyString& str)
  1. 定义新对象,并用已有对象初始化新对象。
MyString t2 = t1;// 拷贝构造
或者是
MyString t2(t1);// 拷贝构造

拷⻉构造函数必须是引⽤传递,不能是值传递。
原因:为了防⽌递归调⽤。当⼀个对象需要以值⽅式进⾏传递时,编译器会⽣成代码调⽤它的拷⻉构造函数⽣成⼀个副本,如果类 A 的拷⻉构造函数的参数不是引⽤传递,⽽是采⽤值传递,那么就⼜需要为了创建传递给拷⻉构造函数的参数的临时对象,⽽⼜⼀次调⽤类 A 的拷⻉构造函数,这就是⼀个⽆限递归。

2.2 拷贝赋值

拷贝赋值即“=操作符重载”:除了以上三种拷贝构造函数的情况,其余将一个对象赋值给另外一个对象都会调用赋值操作符重载。

MyString t1;
MyString t2;
t2 = t1;// 操作符重载

2.3 move构造

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值