2018-05-05 16:53:47 ai_shuyingzhixia 阅读数 341
  • C语言基础》 之 第3章 运算符

    课程内容:表达式、赋值运算符1、赋值预算符2、关系运算符1、关系运算符2、逻辑运算符1 、逻辑运算符2、逻辑运算符3、sizeof运算符1 之 内存结构、sizeof运算符2 之 基本类型回顾 、sizeof运算符3 之 sizeof的使用、逗号运算符 与 符号优先级表

    1786 人正在学习 去看看 张先凤

建立一个字符串类名为String,至少写出三个构造函数,重载赋值等号=、加号+、关系等于==、>、>=、<、<=,另写出析构函数和其他的成员函数,如求字符串的长度、输出字符串等。在main()中定义String类对象测试所写函数是否正确。

#include <iostream>
#include <vector>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
using namespace std;

class Str{
private:
    char *str;
    char length;
public:
    Str():str(NULL), length(0){}
    Str(const Str &s){
        length = s.length;
        str = (char*)malloc((length + 1)*sizeof(char));
        strcpy(str, s.str);
    }
    Str(char *s, int l){
        str = (char*)malloc((l+1)*sizeof(char));
        strcpy(str, s);
        length = l;
    }
    int size(){ return length;  }
    Str& operator+(const Str &s){
        char *ss = (char*)malloc((s.length + length+1)*sizeof(char));
        //length = s.length + length;
        strcpy(ss, str);
        strcat(ss, s.str);
        //free(str);
        //return Str(ss, s.length + length);
        str = ss;
        return *this;
    }
    Str& operator+=(const Str &s){
        char *ss = (char*)malloc((s.length + length + 1)*sizeof(char));
        length = s.length + length;
        strcpy(ss, str);
        free(str);
        strcat(ss, s.str);
        str = ss;
        return *this;
    }
    int operator<=(const Str &s){
        if(strcmp(s.str, str) >= 0 )
            return 1;
        else
            return 0;
    }
    Str& operator=(const Str &s){
        if (this == &s) return *this;
        length = s.length;
        char *ss = (char*)malloc((length + 1)*sizeof(char));
        free(str);
        strcpy(ss, s.str);
        str = ss;
        return *this;
    }

    bool operator==(const Str &s){
        if (s.length != length) return false;
        else return strcmp(s.str, str) == 0;
    }

    friend ostream &operator<<(ostream &out, Str &ss){
        out << ss.str;
        return out;
    }
    ~Str()
    {
        free(str);
    }
};


int main()
{
    char s[100], s2[100];
    strcpy(s, "this is my string");
    strcpy(s2, "this is my house");
    Str str(s, strlen(s));
    Str str2(s2, strlen(s2));
    cout << "(str):\t" << str << endl;
    cout << "(str2):\t" << str2 << endl;
    cout << "(str < str2):\t" << (str<=str2) <<endl;
    cout << "sum(str + str2):\t" << str+str2 << endl;
    cout << "(str):\t" << str << endl;
    cout << "(str2):\t" << str2 << endl;
    str += str2;
    cout << "excute (str += str2)  str = \t" << str << endl;
    cout << "(str2):\t" << str2 << endl;
    str = str2;
    cout << "excute (str = str2)  str = \t" << str << endl;
    cout << "(str2):\t" << str2 << endl;
    Str str4 = str2;
    cout << "excute (str4 = str2)  str2 = \t" << str2 << endl;
    cout << "(str4):\t" << str4 << endl;
    cout << "(str4 < str2):\t" << (str4<=str2) <<endl;
    return 0;
}
2016-11-16 19:30:13 admin_maxin 阅读数 749
  • C语言基础》 之 第3章 运算符

    课程内容:表达式、赋值运算符1、赋值预算符2、关系运算符1、关系运算符2、逻辑运算符1 、逻辑运算符2、逻辑运算符3、sizeof运算符1 之 内存结构、sizeof运算符2 之 基本类型回顾 、sizeof运算符3 之 sizeof的使用、逗号运算符 与 符号优先级表

    1786 人正在学习 去看看 张先凤

    赋值运算符的重载函数基本可以确定为两种写法,一种是初级程序员的写法,另外一种是高级程序员的写法,下面将对赋值运算符的重载函数的两种形式进行解析。


1、初级程序员的写法

    

Test& operator=(const Test& src)
{
     if(this == &src)
     {
        return *this;
     }
     //防止内存丢失和浅拷贝
     delete []_ptr;
     //此处存在问题 
     _ptr = new char[strlen(src._ptr)+1];
     strcpy_s(_ptr, strlen(src._ptr)+1, src._ptr);
     return *this;
}
注意:
此写法存在问题,即 _ptr = new char[strlen(src._ptr)+1];存在问题。问题在于当考虑异常安全性的时候,之前的delete释放了_ptr的内存,若此时的内部不足导致申请内存失败抛出异常,strcpy_s的时候就会导致程序崩溃。


2、高级程序员的写法

Test& operator=(const Test& src)
{
    if (this != &src)
    {
        Test tmp(src);
        char *ptmp = tmp._ptr;
        tmp._ptr = _ptr;
        _ptr = ptmp;
    }
    return *this;
}
    此代码则很好的解决了上边的问题,在上边的代码片中,我先创建了一个tmp的Test类的临时对象,然后通过ptmp指针将tmp._ptr和(*this)._ptr的指向交换,这样当整个函数指向完后局部对象自动析构,这样就能析构调用operator=()函数的对象的原有的内存,不会产生内存丢失,同时解决了异常安全性的问题。Test的拷贝构造函数里调用new分配内存,如果由于内存不足抛出异常,则还没有修改实例((*this)._ptr)的状态,因此实例的状态还是有效的。


    另外,关于赋值运算符的重载函数的返回值为什么是 Test&、形参为什么要加const、形参为什么用引用?这些问题就不再过多陈述,有问题可参考:
    http://www.cnblogs.com/zpcdbky/p/5027481.html



2019-10-19 11:26:15 qq_29819413 阅读数 31
  • C语言基础》 之 第3章 运算符

    课程内容:表达式、赋值运算符1、赋值预算符2、关系运算符1、关系运算符2、逻辑运算符1 、逻辑运算符2、逻辑运算符3、sizeof运算符1 之 内存结构、sizeof运算符2 之 基本类型回顾 、sizeof运算符3 之 sizeof的使用、逗号运算符 与 符号优先级表

    1786 人正在学习 去看看 张先凤

代码1,其中给出了自定义的赋值重载函数:

class test
{
public:
	test()																				// 默认构造函数
	{
		cout << "constructor with argument\n"; 
	}
	~test()																			   // 析构函数 
	{
	}
	test(test& t)																		// 复制构造函数
	{
	 cout << "copy constructor\n"; 
	}
	test&operator=(const test&e)											// 等号重载函数(赋值重载函数)
	{
		cout << "assignment operator\n";  
		return *this;
	}
};
int _tmain(int argc, _TCHAR* argv[])
{
	test ort;
	test a(ort);
	test b = ort ;
	 a = b;
	return 0;
}

第一行:test ort; ort定义为test类,调用默认构造函数;
第二行:test a(ort); a定义为test类,并调用复制构造函数;
第三行:test b=ort;b定义为test类,并用ort初始化b,调用复制构造函数,等同于 test b(ort),用一个类初始化另一个类;
第四行:a = b; 在a已经存在的情况下,用b去赋值a,调用赋值构造函数;
所以实验结果应为:
在这里插入图片描述

要点1:区分两种形式:

先定义类,再去用一个已有的

test b = ort ;  《==》 test b(ort);   区别于 test b; b=sort;

前面两个会调用复制构造函数,后面会调用赋值重载函数;C++内部自带默认的赋值函数,相当于 Empty* operator&(); 如果我们把自定义的赋值函数去掉,也能运行成功。
在这里插入图片描述

要点二:test&operator=(const test&e) 为什么这么写

首先函数返回一个test类型的引用,指的就是a= b中左边的a;然后说明重载的符号是operator =;形参是一个const修饰的test类型引用,说明函数对e不会进行修改;
C++中a = b的含义是在是将b作为operator =(&… )的括号内形参,返回到a上的过程。

用const修饰:

一般地,赋值运算符重载函数的参数是函数所在类的const类型的引用(如上面例1),加const是因为:

①我们不希望在这个函数中对用来进行赋值的“原版”做任何修改。
②加上const,对于const的和非const的实参,函数就能接受;如果不加,就只能接受非const的实参。
用引用是因为:
这样可以避免在函数调用时对实参的一次拷贝,提高了效率。
注意:上面的规定都不是强制的,可以不加const,也可以没有引用,甚至参数可以不是函数所在的对象,正如后面例2中的那样

采用双引用:

一般地,返回值是被赋值者的引用,即*this(如上面例1),原因是
①这样在函数返回时避免一次拷贝(若是值传递,函数运行完要把临时变量再付给a),提高了效率。
②更重要的,这样可以实现连续赋值,即类似a=b=c这样。如果不是返回引用而是返回值类型,那么,执行a=b时,调用赋值运算符重载函数,在函数返回时,由于返回的是值类型,所以要对return后边的“东西”进行一次拷贝,得到一个未命名的副本(有些资料上称之为“匿名对象”),然后将这个副本返回,而这个副本是右值,所以,执行a=b后,得到的是一个右值,再执行=c就会出错。
注意:这也不是强制的,我们可以将函数返回值声明为void,然后什么也不返回,只不过这样就不能够连续赋值了。

代码2,构造函数和赋值重载函数的调用区别

#include<iostream>
#include<string>
using namespace std;
 
class Data
{
private:
    int data;
public:
    Data() {};
    Data(int _data)
        :data(_data)																							// 构造函数,形参是个整形
    {
        cout << "constructor" << endl;
    }
    Data& operator=(const int _data)															// 赋值重载函数,形参是一个常整形
    {
        cout << "operator=(int _data)" << endl;
        data = _data;
        return *this;
    }
};
 
int main()
{
    Data data1(1);
    Data data2,data3;
    cout << "=====================" << endl;
    data2 = 1;
    cout << "=====================" << endl;
    data3 = data2;
    return 0;
}

data2 =1;会调用赋值重载函数
在这里插入图片描述
如果删除自定义的赋值重载函数:
这时调用了构造函数在
这时调用的是构造函数,说明C++内部默认赋值重载函数也不匹配;
可见,当用一个非类A的值(如上面的int型值)为类A的对象赋值
①如果匹配的构造函数和赋值运算符重载函数同时存在(代码2),会调用赋值运算符重载函数。
②如果只有匹配的构造函数存在,就会调用这个构造函数。

参考链接:https://www.cnblogs.com/wlzy/p/10665421.html

2016-03-30 13:57:58 Sico2Sico 阅读数 380
  • C语言基础》 之 第3章 运算符

    课程内容:表达式、赋值运算符1、赋值预算符2、关系运算符1、关系运算符2、逻辑运算符1 、逻辑运算符2、逻辑运算符3、sizeof运算符1 之 内存结构、sizeof运算符2 之 基本类型回顾 、sizeof运算符3 之 sizeof的使用、逗号运算符 与 符号优先级表

    1786 人正在学习 去看看 张先凤

1移动赋值运算符.cpp

#include<iostream>
#include<string.h>
using namespace std;

class my_string{
    char* p=nullptr;
    public:
    my_string(){}
    my_string(const char* s){
        if(s){
            p = new char[strlen(s)+1];
            strcpy(p,s);
        }
    }
    my_string( const my_string& ms){
        cout<<"copy construction"<<endl;
        if(ms.p){
            if(p)delete []p;
            p = new char[ms.size()+1];
            strcpy(p,ms.p);
        }
    }
    ~my_string(){
        if(p)delete [] p;
    }
    my_string(my_string && s)
    {
        cout<<"move construction"<<endl;
        p = s.p;
        s.p =nullptr;
    }
friend ostream &operator<<(ostream&cout,my_string s);
    char & operator[](int pos)
    {
        return p[pos];
    }
    int size()const{return strlen(p);}
    my_string operator+(my_string &s)
    {
        my_string tmp;
        tmp.p= new char[this->size()+s.size()+1];
        strcpy(tmp.p,this->p);
        strcat(tmp.p,s.p);
        return tmp;
    }
    my_string operator+(const char* s)
    {
        my_string tmp;
        tmp.p= new char[this->size()+strlen(s)+1];
        strcpy(tmp.p,this->p);
        strcat(tmp.p,s);
        return tmp;
    }
    my_string &operator=(my_string &&s)
    {
        cout<<"move assignment=(&&)"<<endl;
        p = s.p;
        s.p =nullptr;

    }
};

ostream &operator<<(ostream&cout,my_string s)
{
    cout<<s.p;
    return cout;
}

int main()
{
    my_string s1="123";
    my_string s2="456";
    my_string s3;
    //不要把一个左值轻易转换为右值使用,除非你确定它在下面不再被使用
//err:  my_string s4(std::move(s1));

    //移动赋值运算,提高了运算效率,没有拷贝的过程
    s3 =s1+s2;
    //(a=b)=c;
    int a=1,b=1,c=1;
    (a=b)=c;
    return 0;
}
2014-07-17 12:19:26 CodeAsWind 阅读数 1721
  • C语言基础》 之 第3章 运算符

    课程内容:表达式、赋值运算符1、赋值预算符2、关系运算符1、关系运算符2、逻辑运算符1 、逻辑运算符2、逻辑运算符3、sizeof运算符1 之 内存结构、sizeof运算符2 之 基本类型回顾 、sizeof运算符3 之 sizeof的使用、逗号运算符 与 符号优先级表

    1786 人正在学习 去看看 张先凤

声明类型CMyString,为该类型添加赋值运算符

Class CMyString

{

Public:

    CMyString(char* pData=NULL);

    CMyString(const CMyString& str);

    ~CMyString();

Private:

    Char* m_pData;

};

 

赋值运算符实现要求:

(1):返回值类型声明为该类型的引用,并且在函数结束前返回实例自身的引用(*this)。

why)只有返回一个引用,才能实现赋值运算符的连续赋值操作。假如返回值为void,并且有三个CMyString对象,那么str1=str2=str3,将不能通过编译。

(2):把传入的参数类型声明为常量的引用。如果传入的参数为实例,那么从形参到实参的过程得调用拷贝构造函数。引发不必要的开销。

(3):释放实例自身的内存,如果在分配新的内存,未释放已有的内存,将造成内存泄露

(4):判断传入的参数和当前的实例是否是同一个实例,若是则不进行赋值操作。如果不进行判断,那么在释放已有内存阶段,同时释放了传入实例的内存,下面将要进行的操作将无效。

 

CMyString& CMyString::operator=(const CMyString &str)

{

    If(this==&str)

        Return *this;

    Delete []m_pData;

    M_pData=NULL;

    M_pData=new char[strlen(str.m_pData)+1];

    Strcpy(m_pData,str.m_pData);

    Return *this;

}

C++连续赋值操作

阅读数 867

c++赋值运算符

阅读数 411

没有更多推荐了,返回首页