- const放在该函数前面和后面的区别
// 常量成员函数,这个函数不会修改该类的任何成员数据的值
double avg_price() const;
// const放在函数前面,表示返回值不可修改,用的很少,没什么意思
const double avg_price()
- 类背后蕴含的思想是数据抽象和封装
数据抽象即将接口和实现相分离,封装是一项将低层次的元素组合起来形成新的、更高层次实体的技术.函数是封装的一种形式:函数所执行的细节行为被封装在函数本身这个更大的实体中. - 成员函数和普通函数的区别
1)普通函数指的是C语言的函数,就是不写到class里的函数,这个通常是因为有一些库本身是C语言写的,C++包含C,所以可以用这些函数。
2)C++类里写的函数都是成员函数,但包括静态和非静态(也就是普通的)成员函数,静态的是C++类自己的,用类名加点来访问,普通的是属于对象的,用对象或指针来访问。
3)成员函数和成员变量是同一个层次的,都是为对象服务的,所以在成员函数里是可以访问成员变量的,所以你在函数里操作后,类的成员变量就发生变化了。 - 定义类类型对象
// Sales_item是一个类,下面这两种方法等价
Sales_item item1;
class Sales_item item1;
- 为什么类定义以分号结束
class Sales_item{...};
// 因为在定义之后可以接一个对象定义列表.定义必须以分号结束
class Sales_item{...} accum, trans;
- this指针
// this是一个指向非常量Screen的指针.不能从const成员函数返回指向类对象的普通引用,const成员函数只能返回*this作为一个const引用.
Screen& Screen::set(char c){
contents[cursor] = c;
return *this;
}
- mutable可变数据成员
当希望类的数据成员(甚至是在const成员函数中)可以修改,则可以通过将它们声明为mutable来实现.
class Screen{
public:
private:
mutable size_t access_ctr;
void do_display(std::ostream &os) const{
os<<contents;
}
}
void Screen::do_display(std::ostream& os) const{
++access_ctr;
os << contents;
}
- 如果返回类型使用由类定义的类型,则必须使用完全限定名
class Screen{
public:
typedef std:string::size_type index;
index get_cursor() const;
};
inline Screen::index Screen::get_curcor() const{
return cursor;
}
- 构造函数
构造函数是特殊的成员函数,只要创建类类型的新对象,都要执行构造函数,构造函数的工作是保证每个对象的数据成员具有合适的初始值.构造函数的名字与类的名字相同,并且不能指定返回类型,形参可以有0个或多个. - 构造函数不能声明为const
class Sales_item{
public:
// 错,const构造函数是不必要的,构造函数的工作是初始化对象,不管对象是否是const,都用一个构造函数来初始化该对象.
Sales_item() const;
}
- 构造函数初始化式
从概念上讲,构造函数分为两个阶段执行:
1)初始化阶段
2)普通的计算阶段,计算阶段由构造函数函数体中的所有语句组成.
// 构造函数初始化式只在构造函数的定义中而不是声明中指定
Sales_item::Sales_item(const string &book):
isbn(book), units_sold(0), revenue(0.0) {}
// 在函数体中赋值等价
// 这个构造函数给类Sales_item的成员赋值,但没有进行显式初始化(上面的写法才是初始化,下面的写法是赋值).
Sales_item::Sales_item(const string &book){
isbn = book;
units_sold = 0;
revenue = 0.0;
}
-
必须使用构造函数初始化列表的情况
如果没有为类成员提供初始化式,则编译器会隐式地使用成员类型的默认构造函数.
有些成员必须在构造函数初始化列表中进行初始化,对于这样的成员,在构造函数函数体中对它们赋值不起作用,都必须在构造函数初始化列表中进行初始化:
1)没有默认构造函数的类类型的成员
2)const成员
3)引用类型成员 -
成员被初始化的次序是定义成员的次序
class X{
int i;
int j;
public:
// 先初始化i,再初始化j
X(int val):j(val),i(j){}
}
- 合成的默认构造函数
只要定义一个对象时没有定义初始化式,就使用默认构造函数.
一个类哪怕只定义了一个构造函数,编译器也不会再生成默认构造函数.只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数.
合成的默认构造函数使用与变量初始化相同的规则来初始化成员.具有类类型的成员通过运行各自的默认构造函数来进行初始化.内置和复合类型的成员,如指针和数组,只对定义在全局作用域中的对象才初始化.当对象定义在局部作用域中时,内置或复合类型的成员不进行初始化. - explicit构造函数
通过将构造函数声明为explicit,来防止在需要隐式转换的上下文中使用构造函数.explicit关键字只能用于类内部的构造函数声明上,在类的定义体外部所做的定义上不再重复它.
class Test1
{
public:
Test1(int n)
{
num=n;
}//普通构造函数
private:
int num;
};
class Test2
{
public:
explicit Test2(int n)
{
num=n;
}//explicit(显式)构造函数
private:
int num;
};
int main()
{
Test1 t1=12;//隐式调用其构造函数,成功
Test2 t2=12;//编译错误,不能隐式调用其构造函数
Test2 t2(12);//显式调用成功
return 0;
}
- 友元
友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类.友元的声明以关键字friend开始.它只能出现在类定义的内部.友元声明可以出现在类中的任何地方,通常,将友元声明成组地放在类定义的开始或结尾.
友元可以是普通的非成员函数,或其他类的成员函数,或整个类.将一个类设为友元,友元类的所有成员函数都可以访问授予友元关系的那个类的非公有成员.
class Screen{
// 友元类
friend class Window_Mgr;
// 友元函数
friend Window_Mgr& Window_Mgr::relocate(Window_Mgr::index,Window_Mgr::index,Screen&)
}
- static类成员
static类成员不同于普通的数据成员,static数据成员独立于该类的任意对象而存在.每个static数据成员是与类关联的对象,并不与该类的对象相关联.static成员函数没有this形参,它可以直接访问所属类的static成员,但不能直接使用非static成员.
使用static成员而不是全局对象的优点:
1)static成员的名字是在类的作用域中,因此可以避免与其他类的成员或全局对象名字冲突.
2)可以实施封装.static成员可以是私有成员,而全局对象不可以.
3)通过阅读程序容易看出static成员是与特定类关联的,这种可见性可清晰地显示程序员的意图.
static成员函数: 当我们在类的外部定义static成员时,无需重复指定static保留字,该保留字只出现在类定义体内部的声明处.
static函数没有this指针:
1)static成员是类的组成部分但不是任何对象的组成部分,因此,static成员函数没有this指针
2)因为static成员不是任何对象的组成部分,所以static成员函数不能被声明为const,毕竟,将成员函数声明为const就是承诺不会修改该函数所属的对象
3)static成员函数不能被声明为虚函数
static数据成员的类型可以是该成员所属的类类型,非static成员被限定声明为其自身类对象的指针或引用.
class Bar{
public:
//...
private:
static Bar mem1; // 对
Bar *mem2; // 对
Bar mem3; // 错,非static成员被限定声明为其自身类对象的指针或引用
}
static数据成员可以作默认实参,而非static数据成员不能作默认实参,因为它的值不能独立于所属的对象而使用,使用非static数据成员作默认实参,将无法提供对象以获取成员的值,因而是错误的.
class Screen{
public:
// static数据成员bkground作默认实参
Screen& clear(char = bkground);
private:
static const char bkground = "#";
}
- static数据成员必须在类定义体的外部定义(正好一次)
因为同一类的不同对象的普通数据成员是不同的,而static数据成员是共有的(只有一个),如果static数据成员在类中定义,则每生成一个对象就会对static数据成员定义一次,显然是错误的.
一个例外是,如果初始化式是一个常量表达式,整型const static(同static const)数据成员可以在类的定义体中进行初始化.
class Account{
public:
static double rate();
static void rate(double);
private:
// cosnt static数据成员在类的定义体中初始化时,该数据成员仍必须在类的定义体之外进行定义.
static const int period = 30;
// period是一个常量表达式
double daily_tbl[period];
}
- static关键字只能用与类定义体内部的声明中,定义不能标示为static.