C++ 一些基本语法

explicit

       用处: 禁止隐式类型转换操作
       explicit 它与 virtual、inline 合称为“函数限定符”。但它只适用于构造函数。若一个类拥有只带一个参数的构造函数,则可以隐式转换


explicit作用:

    在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换。

    explicit使用注意事项:explicit 关键字只能用于类内部的构造函数声明上,explicit 关键字作用于单个参数的构造函数。

    在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换。


例子一:

未加explicit时的隐式类型转换

class Circle
{
public:
	Circle(double r) : R(r) {}
	Circle(int x, int y = 0) : X(x), Y(y) {}
	Circle(const Circle& c) : R(c.R), X(c.X), Y(c.Y) {}
private:
	double R;
	int X;
	int Y;
};

int _tmain(int argc, _TCHAR* argv[])
{
 //发生隐式类型转换
 //编译器会将它变成如下代码
//tmp = Circle(1.23)
//Circle A(tmp);
//tmp.~Circle();
	Circle A = 1.23;
 //注意是int型的,调用的是Circle(int x, int y = 0)
 //它虽然有2个参数,但后一个有默认值,任然能发生隐式转换
	Circle B = 123;
 //这个算隐式调用了拷贝构造函数
	Circle C = A;

	return 0;
}

加了explicit关键字后,可防止以上隐式类型转换发生

 class Circle
 {
public:
	explicit Circle(double r) : R(r) {}
	explicit Circle(int x, int y = 0) : X(x), Y(y) {}
	explicit Circle(const Circle& c) : R(c.R), X(c.X), Y(c.Y) {}
private:
	double R;
	int X;
	int Y;
};

int _tmain(int argc, _TCHAR* argv[])
{
 //一下3句,都会报错
 //Circle A = 1.23;
 //Circle B = 123;
 //Circle C = A;
 //只能用显示的方式调用了
 //未给拷贝构造函数加explicit之前可以这样
	Circle A = Circle(1.23);
	Circle B = Circle(123);
	Circle C = A;

 //给拷贝构造函数加了explicit后只能这样了
	Circle A(1.23);
	Circle B(123);
	Circle C(A);
	return 0;
}



例子二:


class   A  
{  
        public:  
            A(int);  
        private:  
            int   num;  
};  
   
int   Test(const   A&)   //   一个应用函数  
{  
        ...  
}  
   
Test(2);   //   正确  
     过程是这样的:   编译器知道传的值是int而函数需要的是A类型,但它也同时知道调用A的构造函数将int转换成一个合适的A,所以才有上面成功的调用.换句话说,编译器处理这个调用时的情形类似下面这样:  
      const A temp(2);   //   从2产生一个临时A对象  
      Test(temp);              //   调用函数  
   
   
如果代码写成如下样子:  
class   A  
{  
        public:  
          explicit   A(int);  
        private:  
            int   num;  
};  
   
int   Test(const   A&)   //   一个应用函数  
{  
        ...  
}  
  
Test(2);   //   失败,不能通过隐式类型转换将int类型变量构造成成A类型变量

例子三:

按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象,如下面所示:
class String {
      String ( const char* p ); // 用C风格的字符串p作为初始化值
     //…
}
String s1 = “hello”; //OK 隐式转换,等价于String s1 = String(“hello”);

但是有的时候可能会不需要这种隐式转换,如下:
class String {
       String ( int n ); //本意是预先分配n个字节给字符串
       String ( const char* p ); // 用C风格的字符串p作为初始化值
       //…
}

下面两种写法比较正常:
String s2 ( 10 );   //OK 分配10个字节的空字符串
String s3 = String ( 10 ); //OK 分配10个字节的空字符串

下面两种写法就比较疑惑了:
String s4 = 10; //编译通过,也是分配10个字节的空字符串
String s5 = ‘a’; //编译通过,分配int(‘a’)个字节的空字符串

s4 和s5 分别把一个int型和char型,隐式转换成了分配若干字节的空字符串,容易令人误解。
为了避免这种错误的发生,我们可以声明显示的转换,使用explicit 关键字:
class String {
       explicit String ( int n ); //本意是预先分配n个字节给字符串
       String ( const char* p ); // 用C风格的字符串p作为初始化值
       //…
}
加上explicit,就抑制了String ( int n )的隐式转换,

下面两种写法仍然正确:
String s2 ( 10 );   //OK 分配10个字节的空字符串
String s3 = String ( 10 ); //OK 分配10个字节的空字符串

下面两种写法就不允许了:
String s4 = 10; //编译不通过,不允许隐式的转换
String s5 = ‘a’; //编译不通过,不允许隐式的转换

因此,某些时候,explicit 可以有效得防止构造函数的隐式转换带来的错误或者误解

----------------------------------------------------------
explicit只对构造函数起作用,用来抑制隐式转换。如:  
class   A{  
          A(int   a);  
};  
int   Function(A   a);  
   
     当调用Function(2)的时候,2会隐式转换为A类型。这种情况常常不是程序员想要的结果,所以,要避免之,就可以这样写:  
class   A{  
          explicit   A(int   a);  
};  
int   Function(A   a);  
   
      这样,当调用Function(2)的时候,编译器会给出错误信息(除非Function有个以int为参数的重载形式),这就避免了在程序员毫不知情的情况下出现错误。
注意:只是用于一个参数的构造函数( 如:1、constructor(typename value); 2、construcor(typename value1,typename value2=defaultvalue,typename value3=defaultvalue,...) ),因为两个参数的构造函数几乎没办法隐式的转换,即无法出现classtype classname = value;的情况(因为这样只能赋给一个值)。

export

为了访问其他编译单元(如另一代码文件)中的变量或对象,对普通类型(包括基本数据类、结构和类),可以利用关键字extern,来使用这些变量或对象时;但是对模板类型,则必须在定义这些模板类对象和模板函数时,使用标准C++新增加的关键字export(导出/出口/输出)。例如:

extern int n;

extern struct Point p;

extern class A a;

export template<class T> class Stack<int> s;

export template<class T> void f (T& t) {……}

一般是在头文件中给出类的定义或全局函数的声明信息,而在代码文件中给出具体的(类成员函数或全局函数的)函数定义。然后在多个用户代码文件中包含该头文件后,就可以使用其中定义或声明的类和函数。头文件中一般不包含变量、结构和类对象的定义,因为这样可能会导致重复定义的编译错误。解决办法是,在某个代码文件中进行定义,在其他用户代码文件中用extern来引用它们。

但是对模板类型,则可以在头文件中,声明模板类和模板函数;在代码文件中,使用关键字export来定义具体的模板类对象和模板函数;然后在其他用户代码文件中,包含声明头文件后,就可以使用该这些对象和函数了。例如:

// out.h:(声明头文件——只包含out函数的声明信息)

template<class T> void out (const T& t);

// out.cpp:(定义代码文件——包含out函数的声明[通过include]和定义等全部信息)

#include <iostream>

#include “out.h”

export template<class T> void out (const T& t) {std::cerr << t;}

//user.cpp:(用户代码文件——包含函数的声明头文件后就可以使用该函数)

#include “out.h”

// 使用out()

说明:VC05目前还不支持export关键字(的编译)。

mutable

在类的常型(const)成员函数中,一般是不让改变类中数据成员的。如果想在常型成员函数中改变类的数据成员,在传统C++中,为达到此目,可采用一种奇怪的方式——先将this指针强制转换成一个本类的指针,然后就可以利用该指针来对类的数据成员进行任意的修改。但是,这种修改是隐藏在成员函数内部的,在类定义(头文件)中根本看不出来,而且它也破坏了设置常型成员函数的本意。

标准C++中新增加了一个关键字mutable(易变/可变/不定/无常的),用在类的数据成员前,明确表示该成员变量可以在常型成员函数中被修改。例如:

class A {

       int i;

       mutable int j;

public:

       void f ( ) const;

};

void A::f ( ) const {

       i++; // 错误——常型成员函数不允许改变数据成员的值

       ((A*)this)->i++; // 可以——已经过时,不被提倡

       j++; // 正确——mutable型成员变量

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值