目录
一初始化列表
1 初始化列表的基本定义
class Date
{
private:
int year;
int month;
int day;
public:
Date(int _year, int _month, int _day)
:year(_year),
month(_month),
day(_month)
{
}
};
int main()
{
Date d(1, 1, 1);
return 0;
}
这样就可以实现成员变量year,month和day的初始化了。
2 初始化列表的本质
初始化列表可以被认为是成员变量被定义的地方。先去完成默认构造,再去赋值给对应的变量。
c++中对于内置的类型不会处理,所以内置的类型一般是随机值。c++后来打了一个对应的补丁,那么对于内置类型的缺省值其实相当于在初始化列表中给的值。当你没有指定对应的值,就是1,1,1,;但是当你传入了对应的值,就是你所传入的值了。
class Date
{
private:
int year=1;
int month=1;
int day=1;
public:
Date(int _year, int _month, int _day)
:year(year),
month(_month),
day(_month)
{
}
};
因此如果有必要的话,我们也可以对这些内置类型在初始化列表的时候进行定义。
3 初始化列表的顺序
初始化列表定义的顺序和初始化列表中的顺序无关,取决于声明的时候的顺序。
class A
{
public:
A(int a)
:_a1(a)
,_a2(_a1)
{}
void Print()
{
cout<<_a1<<" "<<_a2<<endl;
}
private:
int _a2;
int _a1;
}
int main()
{
A aa(1);
aa.Print();
}
有这样一段程序可以说明上述的问题。由于声明是先声明了_a2,再声明了_a1,因此初始化列表也会先去定义_a2,再去定义_a1,因此就与初始化列表中先1后2的顺序没有多大的关系。
这样的一段程序,由于_a2是的定义要依靠_a1,但是此时_a1还没有被定义,是一个随机值,因此_a2也是一个随机值。对于_a1的话,是被a来定义的,因此_a1的值就是a的值,是1.
4 必须用初始化列表的类型
①自定义类型的成员变量
自定义类型的成员变量,编译器会自动对他进行初始化。但是如果对于一个类中有另一个自定义类型的成员变量,但是这个变量他没有默认构造函数,那么编译器也无法对他进行初始化了。比如下面这段程序:
class Time
{
private:
int hour;
public:
Time(int _hour)
{
hour = _hour;
}
};
class Date
{
private:
int year;
int month;
int day;
Time _t;
public:
Date(int _year, int _month, int _day, int _hour)
{
year = _year;
month = _month;
day = _day;
Time t(_hour);
_t = t;
}
};
编译器会报错,没有合适的默认构造函数可以使用。
但是如果使用初始化列表的方式就可以编译通过了,虽然初始化的值默认给的是随机值:
class Time
{
private:
int hour;
public:
Time(int _hour)
{
hour = _hour;
}
};
class Date
{
private:
int year;
int month;
int day;
Time _t;
public:
Date(int _year, int _month, int _day, int _hour)
:_t(_hour)
{
year = _year;
month = _month;
day = _day;
}
};
但是需要注意的是,如果你默认给了的话,即使在函数体内初始化也是可以编译通过的
class Time
{
private:
int hour;
public:
Time(int _hour=10)
{
hour = _hour;
}
};
class Date
{
private:
int year;
int month;
int day;
Time _t;
public:
Date(int _year, int _month, int _day, int _hour)
{
year = _year;
month = _month;
day = _day;
Time t(_hour);
_t = t;
}
};
②const修饰的成员变量
由于const修饰的成员变量是在被定义的时候初始化的,她是一个只读类型的变量。(c++11中新引入的一个标准,让他可以直接在声明的时候给值)
③引用成员变量
引用成员变量也是在定义的时候就被初始化了的,因此也是必须在初始化列表进行初始化。
5 初始化列表和函数内部初始化的比较
对于自定义类型,如果在函数体内初始化的话,需要先拷贝构造形成一个临时变量t,再拷贝构造对_t进行初始化。那倒不如直接在初始化列表进行初始化。
还有一些内置类型她的本质上也是走的初始化列表来进行初始化,那也可以在初始化列表进行初始化,虽然这种类型在函数体内和初始化列表没什么差别。
还有一些必须是在初始化列表初始化的数据类型。
所以总结就是,能用初始化列表进行初始化,最好就用初始化列表进行初始化。
但是对于一些特殊的,比如要去malloc一段空间来开辟数组的这种,直接在初始化列表初始化有点牵强了,可以在函数体内初始化。
它们二者各有对应的场景。
二 explicit关键字
讲这个关键字之前,我们先了解一下:对于一个类来说,如果只有一个成员函数,并且有一个单参数的构造函数,那么如果直接使用“=”,编译器会自动进行隐式类型的转换。
class Date
{
private:
int year;
public:
Date(int _year)
{
year = _year;
}
};
int main()
{
Date d(1);//构造函数
Date d1 = 1;//构造函数+拷贝构造函数-》(优化之后)直接调用构造函数
return 0;
}
explicit这个关键字,就是阻止这个隐式类型的转换发生的
class Date
{
private:
int year;
public:
explicit Date(int _year)
{
year = _year;
}
};
int main()
{
Date d(1);//构造函数
Date d1 = 1;//
return 0;
}
加上这个关键字上述操作就无法发生了。
由于存在上述这个隐式类型的转换,因此string类中可以直接这样实现初始化:
string s = "hello world";
三 匿名对象
生命周期只有对应的这一行。有时候可能只需要在这一行内用到,就会出现对应的匿名对象。
Date(1);
可以直接这样指定。
四 static
1特性:
①静态成员(变量或者函数)存放在静态区,她的生命周期在整个程序的运行期间。为所有的类对象共享。
②在类外定义,无法直接在声明的时候给值,也无法在初始化列表进行定义。类内只是声明。
class Date
{
private:
int year;
static int month;
public:
Date(int _year)
{
year = _year;
}
};
int Date::month = 1;
③可以用::或者.来访问
此时是public的,才能被访问到
cout << Date::month;
cout << d.month;
④他仍然会受到类访问作用限定符的限制。因此对于private这类的成员变量,可以使用static的函数return回对应的值。
class Date
{
private:
int year;
static int month;
public:
Date(int _year)
{
year = _year;
}
static int Getmonth()
{
return month;
}
};
int Date::month = 1;
int main()
{
Date d(1);//构造函数
cout << Date::Getmonth();
return 0;
}
⑤由于他不是属于具体的某个对象的,他是属于整个类的,他没有this指针。(this指针指向当前的对象,只有非静态成员函数才有)
因此,静态成员函数他不能调用非静态的成员函数。
但是非静态成员函数可以调用静态的成员函数,因为非静态成员函数有this指针,他可以调用对应的成员函数。
五 friend 友元
1 分类:
友元函数和友元类
2 友元函数的特性:
①不是类的成员函数,他是在类外定义的一个普通函数,但是在类内声明了是友元的话,就可以访问类的私有和保护成员了。
class Date
{
private:
int year;
friend void show(Date d);
public:
Date(int _year=1)
{
year = _year;
}
};
void show(Date d)
{
printf("%d", d.year);
}
int main()
{
Date d(2011);//构造函数
show(d);
return 0;
}
②不能用const修饰。因为他不是成员函数。
friend void show(Date d) const;
但是如果这个函数本身就是const可以
class Date
{
private:
int year;
friend void const show(Date d);
public:
Date(int _year=1)
{
year = _year;
}
};
void const show(Date d)
{
printf("%d", d.year);
}
③由于是一个类外的普通函数,因此不受类访问限定符的限制,并且可以在类内的任何地方进行声明。
上述例子就是在private中声明,但是最后也能访问。
④一个函数可以是多个类的友元函数。
class Time
{
private:
int hour;
friend void show();
public:
Time(int _hour=10)
{
hour = _hour;
}
int setTime(int b)
{
hour = b;
return hour;
}
};
class Date
{
private:
int year;
friend void show();
public:
Date(int _year=1)
{
year = _year;
}
int setDate(int a)
{
year = a;
return year;
}
};
void show()
{
Date d;
Time t;
int ret=d.setDate(10)+t.setTime(10);
cout << ret;
}
3 友元类的特性
①友元类是另一个类的友元函数,可以访问另一个类的私有和保护成员。
注意:友元函数是单向的,不具有交换性质。并且不能被继承,也不能传递。
六 内部类
把一个类定义在另一个类的内部,叫做内部类。
class Date
{
private:
int year;
public:
Date(int _year=1)
{
year = _year;
}
class Time
{
private:
int hour;
public:
Time(int _hour = 10)
{
hour = _hour;
}
};
};
int main()
{
cout << sizeof(Date);
}
Date的大小与Time无关。
因为这只是声明而非定义,除非用Time再实例化一个对象出来,才有关系。
性质:
内部类受到外部的类域的限制,也会受到访问限定符的限制。
并且内部类天生是外部类的友元,也就是说内部类可以访问外部类的私有变量。