C++学习笔记--1

此文属学习笔记,随学习过程中碰到的问题而不断更新....

1、 在读取相对路径时,GCC认为当前路径为当前的源文件所在目录为当前路径,所以其相对路径皆以此路径为基准;而VC则认为当前工程文件所在目录为当前路径。

       假设当前cpp文件位于“E:\C++”文件夹中,而对应的VC工程在"F:\PROJECT\"中,Data目录位于E盘根目录下。则使用GCC可以读取成功,而VC读取失败。

#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
	string filename_str = "..//Data//World.txt";
	ifstream ifs(filename_str.c_str());
	if(ifs.fail()) 
	{
		cout << "failed" << endl; 
		return 1;
	}
	ifs.close();
	return 0;
}

2、在C++中,自增(++)和自减(--)运算符,是唯一能够同时用于前缀和后缀的运算符。因此对于其重载必须要区分出用于何种方式。(下面以自增++为例)

     (1)一般而言,为了区分出是用于前缀还是用于后缀,可以在重载函数的参数表中加一个虚设的参数来表示用于后缀,以作区别,这里虚设的参数是没有任何的作用,也不会被使用的,仅仅只是作为一个区别的标志,在实际编译过程中,始终为0。【本来以为这个虚设的参数可以是任意类型的,因为反正不会被使用的,测试一下,发现虚设的参数必须是int类型,希望要注意一下】

     (2)在将++作为后缀操作符重载时,返回值必须为const。原因很简单,对一个整数n, n++++是非法的,而++++n合法,因此我们也要杜绝作后缀重载时解析为Object.operator ++ (0) .operator ++ (0)。设置返回值为const后,就可以使得编译时发现错误了(返回值为const型,当然不能在此基础上++啦!)。从下面的代码中,不难看出,我们在使用前缀和后缀的过程中,应该尽量避免使用后缀,原因嘛,就是后缀操作过程中,创建了临时变量,同时它也间接地用到了前缀操作。

【参考:《C++程序设计语言》和《More effective C++》】

下面是测试的代码:

#include <iostream>
using namespace std;


class TestClass
{
public:
    TestClass(const int val = 0) : value(val) {}
    TestClass& operator ++ ();
    const TestClass operator ++ (int m);
private:
    int value;
};
TestClass& TestClass::operator ++ ()
{
	value += 1;
	cout << "using ++value" << endl;
	return *this;
}
const TestClass TestClass::operator ++ (int m)
{
    const TestClass temp = *this;
    ++(*this);
    cout << "using value++" << endl;
	return temp;
}
int main()
{
	TestClass TestObj1;
	int i = 0;
	//i++++; 编译错误,因此在重载时,也要避免这种情况
	TestObj1++;
	++TestObj1;
	return 0;
}

3、C++中对于运算符的重载分为两类:一元运算符和二元运算符。(不允许重载类似于 "a ? b : c"这样的三元运算符)具体的重载是如何呢?

      (1)、对任何二元运算符@,a@b可以解释为a.operator@ (b) 或 operator@ (a, b)。当二者都有定义时,按函数重载的方式来进行匹配;

                    那么,编译器又是如何解析a@b呢?(假设a的类型为A,b的类型为B)

       首先,若A为类,检查其成员函数或A的基类是否有operator @的声明;然后在a@b的环境中查寻operator @的声明;若A在名字空间N里定义,在N里查寻operator @的声明;若B在名字空间M里定义,在M里查寻operator @的声明;(一元运算符与此类似)

      (2)、对于任何一元运算符#,分两类:前缀和后缀。

              【A】、一元前缀运算符情况下,可以表示为#a,解释为a.operator # () 或operator# (a),当二者都有定义时,按函数重载的方式来进行匹配;

              【B】、一元后缀运算符情况下,可以表示为a#,解释为a.operator # (int) 或operator# (a, int),当二者都有定义时,按函数重载的方式来进行匹配,注意,这里的int表示的就是第2条所说的虚设的参数,以表明这是用于后缀运算符。

        事实上,具体例子可以参考ostream类对于 << 的重载:ostream类中有成员函数ostream & operator << (const char *),却没有形如ostream & operator << (const string &)的成员函数,倒是在类外面,有函数ostream & operator << (ostream &, const string &)的声明。

【参考:《C++程序设计语言》】


4、Dereferencing(对间接运算符 -> 的重载)

      首先,应该说明,对于->的重载最重要的是创建“灵巧指针”(smart pointer)。下面的笔记主要是分析这个"smart pointer"类。

    TO BE EDIT

【参考:《C++程序设计语言》、《More effective C++》中item28】


5、explicit声明

       在单变量构造函数中,是允许对象使用隐式构造函数调用的,这样就会很容易地将一个类型转换为另一个类型。

       例:

class test
{
	string name;
	int value;
public:
	test(int value = 0) : name(""), value(0) {}
        //....
};
在后面的函数中,是可以使用test Object = 4;的,这就是隐式调用,而显式调用就是test Object(4);

看到上面的这个例子了没?虽然这种“伎俩”有时是很方便也很cool的,但是,如果你看了<More effective C++>中的那个经典例子Array<int>,我想你会改变你的想法的,例子见下:

template<class T>
class Array
{
public:
    Array(int lowBound, int highBound);//允许调用者确定数组索引的范围
    Array(int size);//能做为类型转换函数使用,能导致无穷的痛苦。
    T& operator[] (int index);
    //...
};
//比较Array<int>对象
bool operator==( const Array<int>& lhs,
                 const Array<int>& rhs)
{
    Array<int> a(10);
    Array<int> b(10);
//...
    for (int i = 0; i < 10; ++i)
        if (a == b[i]) // 哎呦! "a" 应该是 "a[i]"
        {
           // do something for when
            //    a[i] and b[i] are equal;
        }
        else
        {
           // do something for when they're not;
        }
}
因此就有必要避开这种不期望的隐式调用方法了,而要解决这一问题,只需在函数声明的前面加一个explicit,告诉编译器:对这个单变量构造函数,不允许隐式调用,从而从源头上杜绝发生一些莫名其妙的错误。

关于类型转换的一些知识,如重载operator Type () {}这种,如果Type为内置类型,在没有重载opeator <<的情况下,都有可能可以输出一个复杂的类,所以这点也要引起注意,参考《More effective C+》 ItemM5。

【参考:《C++ Primer》、《Programming: Principles and Practice Using C++》、《More effective C++》】

6、类中const成员变量如何定义?

   首先,我们知道,一个类是允许定义const成员变量,但是直接在类中对const成员赋值是会出错的;那在构造函数中呢?提示错误会是const成员不允许更改。这就与我们所要求的const成员变量背道而驰了,既然如此,我们一方面想定义一个不允许被修改的常量,另一方面还希望最好就在类中就能够定义好,那有没有什么解决的方法呢?其实还是有的。。。
   第一种方法:我们可以在类中定义一个enum类型,这样enum{NUM_TO_BE_CONST = ...},而后我们直接在类中使用这个量就OK了。
   第二种方法:定义一个static const成员变量,而且这样的话,我们可以直接在类中进行赋值,或者在类的实现文件中赋值。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值