C++程序设计(下)第一周

来源:互联网 发布:神盾阅读器软件 编辑:程序博客网 时间:2024/06/05 02:42

@(boolan C++)[C++]

1.转换函数(conversion fuction)

转换函数一般是类的成员函数。转换函数的作用是,把class转化成某种类型,也可以把其他类型转换为class。

例如,设计一个函数Fraction,在class Fraction里,定义了一个函数,operator double()const {..}这个函数就是转化函数,它的用处是,如果需要把fraction这个类转为double,就调用这个函数;


class Fraction
{
public:
 Fraction(int num, int den=1)
   : m_numerator(num), m_denominator(den)
 { cout << m_numerator << ' ' << m_denominator << endl; }
 
  operator double() const {
      return (double)m_numerator / m_denominator;
  }

private:  
   int m_numerator;    //
   int m_denominator;  //
};


此处便是转化函数起作用的地方。在第一条语句中,构造了一个对象f,下一条语句中,出现了4+f,这时,f并不是double型,Fraction类也没有重载 加号,编译器本应该报错,但是编译器在Fraction类里头找到了operator double()const {..} 有了这个函数,第二条语句就不会报错,因为编译器会调用这个函数,把f转化为double型。

Fraction f(3,5);
 
 double d = 4 + f;  //4.6
 cout << d << endl;

只要认为合理,类的转换函数可以有多个,转换类型不一定要是基本类型,复合类型也可;


2.non-explict-one-argument ctor

还是用的前面的class Fraction说明。在这里,Fraction的构造函数有两个参数,其中一个有了默认值,默认是1;因此在初始化Fraction对象的时候可以只给一个参数赋值。

class Fraction
{
public:
  Fraction(int num, int den=1)
   : m_numerator(num), m_denominator(den)
 { cout << m_numerator << ' ' << m_denominator << endl; }


  Fraction operator+(const Fraction& f) { 
    cout << "operator+(): " << f.m_numerator << ' ' << f.m_denominator <<  endl;  
    return f;
 }

private:  
   int m_numerator;    //
   int m_denominator;  //
};

在下面的第二条语句中,Fraction重载了加号,但加号左边是一个常量而非Fraction对象,编译器本该报错。但由于Fraction的构造函数可以只有一个参数,所以编译器会自作主张,调用它的构造函数,把4传给第一个参数,这样4+f也是没有错的,下面的语句可以通过。但是如果是第三种情况,即operator double()const {..} 和带默认参数的构造函数同事存在,double b = 4+f就会报错。因为编译器不知道是该将f转化为double型,还是将4转化为Fraction对象。

Fraction f(3,5);
 double d = 4 + f;  //4.6
 cout << d << endl;

但是如果是第三种情况,即operator double()const {..} 和带默认参数的构造函数同事存在,double b = 4+f就会报错。因为编译器不知道是该将f转化为double型,还是将4转化为Fraction对象。所以为了避免出现这种错误,一般会在构造函数前面加一个关键字explict 。它一般用在构造函数或者模板前面,它告诉编译器,构造函数只有在明确表明要调用的时候才可以调用它;这时,编译器就不会把因为遇到4+f这样的情况而去调用它的构造函数了。


3.关于智能指针

template<class T>
class shared_ptr
{
  public:
     T& operator*() const{return *px;}
  T* operator->() const {return px;}
  shared_ptr(T* p): px(p) {}
  private:
     T* px;
  long* pn;
}

struct Foo{... void method(void){} };


上面写了一个智能指针类。下面语句的意思是:new一个Foo之后的指针,当成初值,赋值给一个智能指针;

因为智能指针类里面包含指针,所以凡是智能指针,都会重载*和->;

shared_ptr<Foo> sp(new Foo);

4.模板

C++里面的模板可以分为三大类:类模板、函数模板以及成员模板。

类模板,顾名思义,就是在设计类的时候将其中某个成员变量的类型声明为模板;函数模板在使用的时候不必指明和数据类型;而成员模板指的是结构体或者类模板里面包含的模板;


5.三个主题

(1)variadic templates:数量不定的模板参数

在C++11标准中,新增加了一种语法 ... 这个语法的意思是我不确定我又多少个,所以用...表示省略。

在下面的语句中,我们可以看到template的第一个参数是typename T,第二个参数是typename... Type,这表明设计者在设计初期也不确定其参数个数,只有到了使用的时候才知道。在print函数里, 函数体的第一条语句是输出第一个参数,然后第二条语句,print函数调用自己,参数便是第一次调用print剩下的那些参数,即:const Type&...args ,这些值依次传给print函数,它依次将它们输出。


template<typename T,typename.. Types>

void print(const T&firstArg,const Types&...args)
{
   cout<<firstArg<<endl;
   print(args...);
}

测试:
print(7.5,"hello",bitset<16>(377),42);
结果:
7.5
hello
0000000101111001
42


(2)auto关键字

一般来说,如果我们声明一个容器,并且需要用到迭代器,那么,应该像下面这样写:

list<string> C;
list<string>::iterator it;
it = find(c.begin(),c.end(),target);


但是如果觉得写那么多太麻烦,还可以用auto关键字简化,写成下面这样。auto关键字用处是,设计者可以不明确写出变量的类型,编译器会根据上下语句自行判断。

就像下面的语句,find会返回一个迭代器,所以编译器知道it的类型是迭代器,因此我们不必像上面那样将it声明为迭代器。

但是要注意的是,尽管用auto很省事,但是不能全部用它,否则很容易出现混乱。


list<string> c;
...
auto it = find(c.begin(),c.end(),target);


还有一种情况,下面的写法是不正确的。auto只能用在编译器可以明确判断出变量的类型的地方,而不能用来声明变量。用auto声明变量,编译器也不会知道这个变量是什么类型的。

list<string> c;
auto it;
it = find(c.begin(),c.end(),target);(不正确)

(3)ranged-banse for(C++11)

这也是C++11的新语法

for(decl : coll)
{statement}

for(int i : {2,3,4,5,6,7,8})
{cout << i << endl;}


6.引用 --用法小结

(1)引用一定要赋初值;
(2)引用一旦绑定对象后将不能再绑定到其他对象;

(3)引用是对象的一个代表 ,所以引用的地址、大小都和对象相同,但都是假象;

(4)我们很少用引用声明变量,一般用于参数类型和返回类型;

(5)下面被视为有“相同签名”(所以二者不能同时存在),这是因为这两个函数在调用的时候语法是一样的,都是 image(im);
double image(const double& im){...}
double image(const double im){...}

如果同时声明了这两个函数,那么在调用次函数的时候,会产生二义性;

不过如果加了const就不会了,const也是函数签名的一部分,函数签名有没有const是不一样的。








































阅读全文
0 0
原创粉丝点击