zoukankan      html  css  js  c++  java
  • C++学习笔记-隐式成员函数

    通过一个例子来复习C++操作符重载及隐式成员函数。MyString类模仿标准string类,简单的实现了构造一个字符串、字符串比较、取单个字符等功能。如下:

    #ifndef MYSTRING_H_
    #define MYSTRING_H_
    #include <iostream>
    using std::ostream;
    class MyString
    {
    public:
    	//默认构造函数
    	MyString(void);
    	//析构方法
    	~MyString(void);
    	//构造函数
    	MyString(const char *s);
    	//复制构造函数
    	MyString(const MyString & s);
    	//重载赋值操作符
    	MyString & operator=(const MyString & st);
    	MyString & operator=(const char * s);
    
    	//友元函数非成员函数,重载<<操作符
    	friend ostream & operator<< (ostream & os, const MyString & str);
    
    	//返回字符串长度
    	int length() const{return len;};
    	//将比较函数作为友元,使String对象可以与常规的C字符串进行比较
    	// if("cm" == st)  -> if (operator==("cm",st)) -> if (operator==(MyString("cm"),st))
    	friend bool operator< (const MyString & st, const MyString & dst);
    	friend bool operator> (const MyString & st, const MyString & dst);
    	friend bool operator== (const MyString & st, const MyString & dst);
    
    	//使用[]表示法来取字符
    	//返回 char & 便可以给特定元素赋值 MyString st = "cm"; st[0] = 'j';
    	char & operator[] (int i){return str[i];};
    	//给const MyString对象使用的[]版本
    	const char & operator[](int i) const {return str[i];};
    
    	//静态成员方法,属于类,只能访问静态成员变量
    	static int HowMany(){return num_strings;};
    
    
    private:
    	char *str;
    	int len;
    	//不能在类声明中初始化静态成员变量 
    	static int num_strings;
    };
    #endif
    
    //MyString.cpp
    #include <iostream>
    #include "MyString.h"
    using std::cout;
    
    int MyString::num_strings = 0;
    
    MyString::MyString(void)
    {
    	len = 0;
    	str = new char[len + 1];
    	str[0] = '';
    	num_strings++;
    }
    
    
    MyString::~MyString(void)
    {
    
    	--num_strings;
    	delete [] str;
    }
    
    MyString::MyString(const char *s)
    {
    	len = std::strlen(s);
    	str = new char[len + 1];
    	std::strcpy(str,s);
    	num_strings++;
    }
    
    MyString::MyString(const MyString & s)
    {
    	num_strings++;
    	len = s.len;
    	str = new char[len + 1];
    	std::strcpy(str,s.str);
    }
    
    MyString & MyString::operator=(const MyString & st)
    {
    	//防止将对象赋给自身
    	if (this == &st)
    		return *this;
    	delete [] str; //先释放原有的字符串
    	len = st.len;
    	str = new char[len + 1];
    	std::strcpy(str,st.str);
    	return *this;	//返回自身引用,以便可以连续赋值,eg:S0 = S1 = S3
    }
    
    MyString & MyString::operator= (const char * s)
    {
    	delete [] str; //先释放原有的字符串
    	len = std::strlen(s);
    	str = new char[len + 1];
    	std::strcpy(str,str);
    	return *this;
    }
    
    ostream & operator<< (ostream & os, const MyString & str)
    {
    	os << str.str;
    	return os;
    }
    
    bool operator< (const MyString & st, const MyString & dst)
    {
    	return (std::strcmp(st.str,dst.str) > 0);
    }
    bool operator> (const MyString & st, const MyString & dst)
    {
    	return (dst < st);
    }
    bool operator== (const MyString & st, const MyString & dst)
    {
    	return (std::strcmp(st.str,dst.str) == 0);
    }
    

     隐式成员函数

    C++自动提供以下成员函数:

    1、默认构造函数,如果没有定义构造函数

    2、复制构造函数

    3、赋值操作符

    4、默认析构函数

    5、地址操作符

    其中隐式地址操作符返回调用对象的地址(this指针),一般不考虑它,默认析构函数不执行任何操作。重点关注前三个成员函数。

    (1)默认构造函数

    如果没有提供任何构造函数,C++将创建默认构造函数,如果定义了构造函数,C++将不再定义默认的构造函数,默认构造函数一般是不带参数的,但带参数的构造函数也可以是默认构造函数,只要所有的参数都有默认值。

    (2)复制构造函数

    复制构造函数用于将一个对象复制到新创建的对象中,原型如下:

    Class_name(const Class_name &);

    当函数按值传递对象或函数返回对象时,都将使用复制构造函数。

    新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用,这可能取决与编译器的实现。

    默认复制构造函数的功能:

    逐个复制非静态成员(浅复制)

    当类中包含了使用new初始化的指针成员时,默认复制构造函数就会出现问题,这时,应当显式定义一个复制构造函数,以复制指向的数据(深度复制)。如MyString类的复制构造方法:

    MyString::MyString(const MyString & s)
    {
    	num_strings++;
    	len = s.len;
    	str = new char[len + 1];
    	std::strcpy(str,s.str);
    }
    

     (3)赋值操作符

    C++允许类对象赋值,这是通过自动为类重载赋值操作符实现的。原型如下:

    Class_name & Class_name::operator=(const Class_name &);

    当已有的对象赋给另一个对象时,将是使用重载的赋值操作符。

    赋值操作符的隐式实现也是对成员逐个进行复制。当默认赋值操作符不合适的时候,就应当显式定义赋值操作符。

    如:

    MyString & MyString::operator=(const MyString & st)
    {
    	//防止将对象赋给自身
    	if (this == &st)
    		return *this;
    	delete [] str; //先释放原有的字符串
    	len = st.len;
    	str = new char[len + 1];
    	std::strcpy(str,st.str);
    	return *this;	//返回自身引用,以便可以连续赋值,eg:S0 = S1 = S3
    }
    

     自定义赋值操作符应注意一下事项:

    1)由于目标对象可能引用了以前分配的数据,所以应当先释放这些数据

    2)函数应当避免将对象赋给自身

    3)返回一个指向调用对象的引用,以便可以进行连续赋值。

    总结:

    当类中含有指针成员时,应当注意的事项:

    1、如果在构造函数中使用new来初始化指针成员,则应当在析构函数中使用delete来释放

    2、new与delete必须相互兼容,new对应delete, new[] 对应delete[]

    3、应当自定义一个复制构造函数,进行深度复制。

    4、自定义赋值操作符,通过深度复制将一个对象复制给另一个对象。

    该函数应当完成以下功能:

    检查自我赋值情况,释放成员指针以前指向的内存,复制数据而不仅仅是数据的地址,要返回一个指向调用对象的引用。

  • 相关阅读:
    JDBC的初步了解及使用
    HTML+CSS的小实例
    java接口与抽象类
    java类的继承(基础)
    java中求质数(素数)的问题
    对HTML5标签的认识(四)
    对HTML5标签的认识(三)
    对HTML5标签的认识(三)
    SublimeText2 快捷键一览表
    Node开发项目管理工具 Grunt 对比 Gulp
  • 原文地址:https://www.cnblogs.com/cmranger/p/4106573.html
Copyright © 2011-2022 走看看