一、定义抽象数据类型
- 每一个非静态成员函数都有一个隐式的this形参,当某个对象调用成员函数时,其实是通过类名调用成员函数,然后把对象地址传递给this形参。
- 常量对象不能调用普通的成员函数。这是因为普通成员函数中隐式形参this是一个指向非常量的常量指针,也就是说它指向的地址不变,但是地址所在内存中的数据可以发生变化。但是常量对象的指针是指向常量空间的,也就是对象所在内存数据不能发生变化。所以不能将常量对象地址赋给非指针常量。具体解释如下:
- this解指针以后返回对象的引用。
构造函数
- 我们创建const对象时,直到构造函数完成初始化过程,对象才能取得其const属性,也就是说构造函数在const对象构造构成中可以向其写值。
- 只有在类没有定义任何构造函数的情况下,编译器才会为类定义默认构造函数。也就是说,如下代码会出错:
#include <iostream>
using namespace std;
class MyClass{
private:
int val_;
MyClass(int val):val_(val){}
};
int main()
{
MyClass mycls;//no matching function for call to ‘MyClass::MyClass()’
return 0;
}
- 合成默认构造函数按照如下规则处理类的数据成员:
- 如果有初始值,那么用初始值初始化。
- 如果没有初始值,并且是内置类型或者复合类型,那么其未定义。如果是用户定义类型,则使用其默认构造函数来初始化。
如下类中两个数据成员,val_是未定义,也就是它的值是随机的。
class MyClass{
public:
int val_;
string str_;
};
- 下面的声明是说,默认构造函数使用系统生成的默认构造函数。-c++11新特性。
拷贝、赋值和析构
- 友元函数需要在类外部进行再次声明。
#include <iostream>
using namespace std;
class MyClass{
private:
int val_;
string str_;
public:
MyClass(int val = 0,string str = ""):val_(val),str_(str){}
friend void print(ostream &os, MyClass& cls)
{
os<<cls.val_<<" "<<cls.str_<<endl;
}
friend MyClass add(MyClass& cls1, MyClass& cls2)
{
MyClass res;
res.val_ = cls1.val_ + cls2.val_;
res.str_ = cls1.str_ + cls2.str_;
return res;
}
};
void print(ostream &os, MyClass& cls);
MyClass add(MyClass& cls1, MyClass& cls2);
int main()
{
MyClass cls(100, "123"), cls2(10,"456");
cls = add(cls,cls2);
print(cout,cls);
return 0;
}
类的其它特性
- 定义一个类型成员:可以在类内部使用typedef或者using定义一个类型别名,但是需要注意,这个类型的定义必须在使用之前。
class Screen{
public:
typedef size_t pos;
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents = "";
};
- 不再初始化列表中的成员数据将会被赋予默认初始值(注意不是值初始化),如果没有类内初始值,那么需要显示赋值。
mutable
可以让任何变量永远不会是const的。- 返回类型声明为引用,然后返回
*this
的好处是好几个成员函数可以连续调用,比如Obj.fun1().fun2().fun3
。
- 内联函数的优点,运行时开销更小。
- 类在只声明未定义的情况下,可以使用其引用和指针,不能建立对象。
- 将一个类声明为另一个类的友元。
- 每个类自己决定允许哪些函数或者类来访问自己的私有成员。
- 类的成员函数可以调用非成员函数。
- 类成员函数体是在类整个可见后才进行处理,处理过程中,如果名字所在块中没有找到相应的声明,那么会继续向外层查找,如果一直没有找到,那么保存未定义。
- 使用作用于外的变量:
构造函数再探
构造函数初始值列表
- 如果数据成员是引用、const类型或者是未提供默认构造函数的类类型,那么我们必须在初始化列表对其进行初始化。
- 初始化顺序和成员声明顺序有关和初始化列表顺序无关。
- 下列行为是不合法的,会导致有两个默认构造函数:
#include <iostream>
using namespace std;
struct X{
X(int i=10, int j = 20):base(i),rem(base%j){}
X(string str = ""){}
int rem,base;
};
int main()
{
X x(1,2);
X xa;
return 0;
}
- 委托构造函数:
#include <iostream>
using namespace std;
class MyClass{
private:
int val_;
string str_;
public:
MyClass(int val, string str):val_(val), str_(str){
cout<<"1"<<endl;
}
MyClass(int val):MyClass(val,""){
cout<<"2"<<endl;
}
MyClass():MyClass(0,""){
cout<<"3"<<endl;
}
};
int main()
{
MyClass mcls;
cout<<endl;
MyClass cls1(1,"123");
cout<<endl;
MyClass cls(3);
return 0;
}
隐式类类型转换
- 允许一步类类型转化:
如果说类定义了接收某种类型的构造函数,那么接收类引用的函数,在调用时可以直接传入构造函数需要的类型
#include <iostream>
using namespace std;
class MyClass{
public:
MyClass(){}
MyClass(string str){//前面加上explicite可以抑制隐式调用
cout<<"Hi"<<" "<<str<<endl;
}
void fun(const MyClass& cls){}
};
int main()
{
string str = "123";
MyClass mycls;
mycls.fun(str);
}
聚合类
- 符合下面条件的类称为聚合类
- 所有的成员都是public的
- 没有定义任何构造函数
- 没有类内初始值
- 没有基类也没有virtual函数 - 使用初始化列表来初始化成员数据。
#include <iostream>
using namespace std;
struct MyStruct{
int val_;
string str_;
};
int main()
{
MyStruct msru = {1,"23"};
return 0;
}
字面值常量类
类的静态成员
#include <iostream>
using namespace std;
class MyClass{
public:
static int s_val_;
string str = "123";
public:
void myFunc()
{
cout<<"NoStatic"<<s_val_<<endl;
}
static myStaticFunc()
{
cout<<"static"<<str<<endl;//出错
}
};
int MyClass::s_val_ = 20;
int main()
{
cout<<MyClass::s_val_<<endl;
MyClass cls;
cls.myFunc();
return 0;
}