zoukankan      html  css  js  c++  java
  • 函数条款明明白白c++ effective c++ 条目1620

    每日一贴,今天的内容关键字为函数条款

        

    effective c++的下载地址

    http://download.csdn.net/detail/mlkiller/5335383

        

    前言

        这几天看来一些学习c++的书籍推荐。

        effective c++ 是很多人推荐的,这个也是学习c++的必读之书。

        但是个人觉得这本书可能不是特别合适初学c++的人去学习,因为这里头讲的还是比较深,初学者很多精力都会合在编译错误,或者循环控制等等上面,对于语言的细节,还一些精巧的计划反而会让初学者有些害怕,一方面读不懂这本书讲的什么意义,另一方面不明白effective在哪里。

        其实,刚开始看这本书的时候,我也被名字直观翻译所混淆,因为这本书会介绍如何进步代码的运行效率,但是读完以后,发明所谓的effective的含意应该是狭义的,对于编程呢,我觉得应该有这几点都可以叫做 effective,

        第一,代码易读,其实代码是给人看的,这个其实是最主要的一点。

        第二,代码的正确性,这本书讲了很多常犯的错误,少犯错误就是进步效率。

                   调试代码,甚至测试代码消费的时间,往往比写代码的时间要多。

        第三,保障你自己写的代码的健壮性,这本身上面讲了很多这方面的问题,有种错误叫不可以预知,就是不是必先的错误确切很难查找。

        第四,进步c++自身的效率,比如使用常引用作为函数参数。

        

        

    条款16: 在operator=中对全部数据成员赋值

        

    看到这个条款,我的第一印象是,赋值构造函数肯定会对全部数据成员赋值的,这个条款看似过剩的。

        

    但是看了文章中写的才明白,主要有两个目的:

        

    第一,写赋值构造函数的时候,要注意,包含后来维护的人,如果新加元素,记得在赋值构造函数里头赋值。

        

    第二,这里主要强调的,继承类(子类)的时候,赋值构造函数不必忘失落父类的构造函数,直接上例子。希望大家也编写一下。

        


    #include<iostream>
    using namespace std;
    
    class A
    {
    public:
    	A(int a);
        A& operator=(const A &a);	
        virtual void print();
    private:
    	int data;
    };
    
    A::A(int a):data(a)
    {
    }
    
    A& A::operator=(const A &a)
    {
    	if(this == &a)
    		return *this;
    
    	data = a.data;
    	return *this;
    }
    void A::print()
    {
    	cout<<"A"<<data<<endl;
    }
    
    class B:public A
    {
    public:
    	B(int i);
    	B& operator=(const B& b);
        virtual void print();
    private:
    	int data;
    };
    B::B(int i):A(i),data(i)
    {
    }
    B& B::operator= (const B& b)
    {
    	if (this == &b)
    		return *this;
    
    	data = b.data;
    }
    void B::print()
    {
    	A::print();
    	cout<<"B"<<data<<endl;
    }
    
    class C:public A
    {
    public:
    	C(int i);
    	C& operator=(const C& c);
        virtual void print();
    private:
    	int data;
    };
    C::C(int i):A(i),data(i)
    {
    }
    C& C::operator= (const C& c)
    {
    	if (this == &c)
    		return *this;
    
    	static_cast<A&>(*this) = c;
    	data = c.data;
    }
    void C::print()
    {
        A::print();
    	cout<<"C"<<data<<endl;
    }
    
    int main()
    {
    	B b1(1);
    	B b2(2);
    
    	b1 = b2;
    	cout<<"-----b1 value--------"<<endl;
    	b1.print();
    	cout<<"-----end--------"<<endl;
    	C c1(1);
    	C c2(2);
    	c1 = c2;
    	cout<<"-----c1 value--------"<<endl;
    	c1.print();
    	cout<<"-----end--------"<<endl;
    }


    这个例子有两点须要注意:

        

    1 继承类怎么访问基类的虚函数。

        

       A::print(),这样调用才可以,我开始的设法是先把继承类转换为基类的指针或者引用,再去调用函数。

        

       但是这个有个显著的问题,因为虚函数本身的含意就是用基类的指针或者引用去调用的,这样就等于是死循环。(自己一直调用自己)

        

    2 继承类怎么访问基类的成员变量。

        

        这个就是先转换为基类的指针或者引用,然后访问。其实非虚函数也可以这么访问。

        


        

    打印出来的结果是:

        

    -----b1 value--------
    A1
    B2
    -----end--------
    -----c1 value--------
    A2
    C2
    -----end--------

        


        

    第一个的基类没有被改变。

        


        

    条款17: 在operator=中检查给自己赋值的情况

        


        

    必须检查的,就是成员变量的指针指向统一块内存。本文只讲这种情况。

        

    还有一种是选择检查的类对象相等。(仅仅是内容相等)

        


        

    这个目的有两个:
        每日一道理
    虽然你现在还只是一株稚嫩的幼苗。然而只要坚韧不拔,终会成为参天大树;虽然你现在只是涓涓细流,然而只要锲而不舍,终会拥抱大海;虽然你现在只是一只雏鹰,然而只要心存高远,跌几个跟头以后,终会占有蓝天。

        

    第一,为了效率。 (上面两个都可以进步效率)

        

    第二,保障正确性。(只是针对第一个)

        


        

    大家看上面的例子
    #include<iostream>
    #include<string.h>
    using namespace std;
    
    class MyString
    {
    public:
           MyString(const char *str=NULL);
    	   MyString& operator=(const MyString &str);
    	   friend ostream& operator<<(ostream &os, const MyString &str);
    private:
    	   char *data;
    };
    
    MyString::MyString(const char *str)
    {
    	if (NULL == str)
    	{
    		data = new char[1];
    		data = '\0';
    	}
    	else
    	{
            data = new char[strlen(str)+1];
    		strcpy(data, str);
    	}
    }
    
    MyString& MyString::operator=(const MyString &str)
    {
    	//if (this == &str)
    	//{
    		//return *this;
    	//}
    
    	delete []data;
    	data = new char[strlen(str.data) + 1];
    	strcpy(data,str.data);
    
    	return *this;
    }
    
    ostream& operator<<(ostream &os, const MyString &str)
    {
    	os<<str.data;
    	return os;
    }
    int main()
    {
    	MyString a = "abc";
    	MyString &b = a;
    	a = b;
    	cout<<a<<endl;
    }
    

    注释失落的就是判断指向内存相等。
    但是发明个奇异的问题,

        

    我在cygwin里头编译打印出来的结果居然还是abc,而且单步调试,发明delete的时候指向的内存也没有改变。

        

    开始我怀疑代码有问题,于是在vs2010里头编译了一下,发明运行的结果就是乱码了。

        


        

    所以猜想cygwin里头不是没有问题,只是还没有暴发,所以在a=b前面加了一句话int *str = new int[1000];这个时候重新运行就发明乱码了。

        

    可能是g++编译的时候发明没有其他内存请求,就没有释放。但是指针的巨细已清空了。详细原因可能还须要高人解释一下。

        


        

    有人说你真正能理解上面的这几个函数的写法,你就把握了60%的c++.

        


        

    条款18: 争夺使类的接口完整并且最小

        


        

    我觉得这个条款其实很大,包含的内容应该很大,怎么来计划好一个类。

        

    总结的也很好,就是根据实际情况,使得接口完整,功能齐全,外部使用的都定义为接口。

        

    并且最小,就是简练的准则,没有用的就摒弃失落。

        


        

    剩下的可能要大家到详细项目的时候,再慢慢揣摩这句话的意思。

        


        

    这个有点计划模式的滋味了,后面写一篇文章关于计划模式。

        


        

    条款19: 分清成员函数,非成员函数和友元函数

        

    对于成员函数和非成员函数:如果有虚函数的需求,必须是成员函数。

        

    非成员函数和友元函数的准则,尽可能罕用友元函数。

        

    那么什么情况下必须用有元函数呢?就是你要访问类的私有成员的时候。如果仅仅用接口就可以完成,则不必要用有元函数。

        


        

    上面将书上关于有理数乘法(分数和整数)写一下。

        


    #include <iostream>
    using namespace std;
    
    class rational
    {
    public:
           rational(int numerator = 0,int denominator =1);
    	   int num() const;
    	   int denom() const;
    	   friend ostream &operator<<(ostream &os,rational &rat);
    private:
    	int numerator;
    	int denominator;
    };
    
    
    rational::rational(int num, int denom)
    {
    	numerator = num;
    	denominator = denom;
    }
    
    int rational::num() const
    {
    	return numerator;
    }
    
    int rational::denom() const
    {
    	return denominator;
    }
    
    ostream& operator<<(ostream &os, rational &rat)
    {
    	os<<"num"<<rat.numerator<<"denom"<<rat.denominator;
    	return os;
    }
    
    rational operator*(rational& input1, rational &input2)
    {
    
    	return rational(input1.num()*input2.num(),
    			        input1.denom()*input2.denom());
    }
    
    int main()
    {
    	rational input1(2,3);
    	rational input2(3,4);
    
    	rational output1;
    	rational output2;
    	
    	output1 = input1*input2;
    	output2 = input1*2;
    
    	cout<<output1<<endl<<output2<<endl;
    }
    

    但是有个奇异的地方 output2 = input1 *2;这句话编译不通过,我还没找到原因,编译的错误是无法将int的转换为rational的类型,有点怪异。

        

    有元函数的话,可以看看我之前全部的重载<<的函数,都用的是有元函数。

        


        

    条款20: 避免public接口出现数据成员

        

    这个条款就不解释了。

        

    功能和数据要离开。

        


    文章结束给大家分享下程序员的一些笑话语录: 问:你觉得让你女朋友(或者任何一个女的)从你和李彦宏之间选一个,你觉得她会选谁?  
      答:因为李艳红这种败类,所以我没女友!

  • 相关阅读:
    Visual Studio的输出窗口上输出调试信息的函数
    std::min error C2059: 语法错误:“::” 的解决方法
    error C2872: “flann”: 不明确的符号 --- PCL 与OpenCV2 的flann命名空间冲突问题的解决方法
    VS编译器中设置 输出窗口 只显示error,不显示warning 要如何配置
    nginx.conf的完整配置说明
    Nginx基本配置、性能优化指南
    Apache手册
    Apache 配置虚拟主机三种方式
    Linux常用命令汇总
    Linux下安装Apache
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3074262.html
Copyright © 2011-2022 走看看