先说说面向对象思想的一个总体认识
对象通常会有行为,这些行为是靠信息支撑,这些信息包括外部信息和内部信息,对象行为会维护其中的一部分信息
因此对象可以看成是这样一种实体,它获取信息,然后决定自己的行为方式,并主动对全部或部分信息进行维护。此外
对象的定义可以是递归的,即行为可以涉及到若干个更小功能的对象,信息也可以涉及多个更小功能的对象
1.静态数据成员必须初始化
2.只有静态常量整数型成员才可以在类中初始化,即cont static int a;才可以
3.虚函数 virtual void fun(){}
4.纯虚函数 virtual void fun(){}=0
5.虚基类 class A:virtual public B{}
5.常成员函数 void fun(){} const
不会改变对象的数据成员,总之,它是获取对象的状态而不能改变对象的状态
const代表的是只读特性 static指的是生存周期
6.常对象 const Student stu;Or Student const stu;
7.友元函数 class A{friend void fun(){}} void fun(){}
8.友元类 class A{friend class B};B可以方位A的私有成员和保护成员。
9.protected 受保护成员 所有后代类(不论什么层级)都可以访问。
10.虚函数表
11.复制构造函数 A(const &A obj){}//const意即仅仅是复制,不需要改变被复制对象的状态
用途:1)返回值是一个对象 2)对象作为参数 3)初始化另一个对象
12.构造函数初始化列表
四种情况必须用,1.object member 2.const data member3. Reference type member4.baseclass contructor
CClassA::CClassA(): x(0), y(1){}
13.派生类构造函数
须以参数列表的形式初始化的是对象成员,基类,派生类可列表可函数内初始化,基类的构造函数不能在派生类中重载
14.对象的数据成员和函数成员是分开存放的,并且即使程序中没有建立类的对象,但该类的函数还是会存在于程序中
15.typeid
通常情况下,类型名称的是在编译时就完成了,涉及到多态,或者说有虚函数存在的情况下,并不在编译时确定类型名称,而是
引入虚函数表,这时类的指针不指向对象的第一个数据成员,而是指向虚函数表,相关的类型名称作为该表的一条记录出现
这条记录指向了类名称的字符串,下面是一个例子:
class B
{
virtual fun(){cout<<"This is a base class"<<endl;}
};
class C:public B
{
void fun(){cout<<"This is a derived class"<<endl;}
void fun2(){}
};
//main function
C c;
B *p;
p=&c;
cout<<typeid(p).name()<<endl;//output:class B *
cout<<typeid(*p).name()<<Endl;//output:class C
p->fun()//output:This is a derived class
p->fun2()//error:can't operate a class C's member function through a type B pointer
16.关于virtual function
只要是virtual function,一定会通过虚函数表调用
先说一下虚函数表的内存结构,对于一个有虚函数表的派生类对象的内存起始地址,首先是存放了其虚函数表的地址,然后是数据成员,如果派生类继承自多个基类,那么按照继承的顺序排列基类的(虚函数表地址+数据成员),最后存放派生类的数据成员,新增加的虚函数会放在第一个虚函数表后面。再说一下C++中关于类对象的类型转换机制:
static_cast:
会改变内存结构,自下而上的转换是安全的,自上而下的转换是不安全的,因为这种转换不做安全检查。
dynamic_cast:
会改变内存结构,这是一种类型安全的转换,除了虚函数表,C++编译器还保留了类的类型信息(位于虚函数表向上偏移若干字节),转换时会检查这些类型信息,至于内存中怎么存放这些类型信息以及怎么做检查,这些不同编译器会有不同的实现。
reinterept_cast:
不会改变内存结构,就像他的字面意思,只进行重新解释。
下面的转换都是static_cast
class ParentClass { public : virtual void foo() {/*...*/}; };
class DerivedClass : public ParentClass { public : virtual void foo() {/*...*/};}; DerivedClass derived;
ParentClass parent = (ParentClass)derived;
ParentClass* pObj = &parent;
pObj->foo();
class base
{
public:
virtual void fun()
{printf("bas");} };
class derive:public base
{public:
void fun()
{printf("child");} };
class CC:public derive
{
public:void fun()
{printf("another");}
};
base *p;
CC c;
p=&c;
p->fun().//output another
参考:http://www.cnblogs.com/zhyg6516/archive/2011/03/07/1971898.html
此外static dynamic之所以这样命名也符合这两种转换的性质,static是在编译时期就确定了的,并且已经转换好了,但是他不保证安全,需要开发者自己保证安全,dynamic是运行时进行的转换,通过获取类型的信息,来决定如何转换,转换不成就返回null,因此是安全的,所谓的安全就是不会试图去访问意料之外的内存。
c++提供<typeinfo.h>和typeid来动态获取对象的类型信息
17.在线编译器codepad.org http://ideone.com/ cpp.sh
18.关于<< >>为什么不能重载为成员函数,这里指的是istream ostream之外的类需要重载<<和>>的时候是不适合将其重载为成员函数的,因为如果重载为成员函数,函数的默认的第一个参数是this指针,而第一个参数又对应着运算符的左操作数,在使用的时候就是CObj>>cout,这样可以达成对CObj的个性化输出,但是无法进行串连式的流操作,需要注意的是,这里所说的不能重载为成员函数是对于标准输出输入流之外的类而言的,事实上istream 和 ostream 也是将<<>>重载为成员函数的。
19.函数默认参数,规定有默认值的参数全部放在列表右边,如:int fun(int a=0,int b,int c)不合法,而int fun(int b,int c,int a=0)合法。
20.cplusplus网站上对于类的基类只列出了从中继承的function,数据成员并没有列出来,这一点要注意,但是类自身的数据成员是在左侧列出来的。
21.c++嵌套类的使用是必须要带作用域的,比如class A{class B{};};使用B类型时一定要用A::B才可以。
22.类模版的偏特化,其实叫做类模版的重载也比较合适
23.右值引用
vector<int> getv()
{
vector<int> v;
return v;
}
int main()
{
vector<int> tmp=getv();
return 0;
}
上面的程序按理说最多发生二次对象的拷贝
1.getv返回的时候发生一次(虽然这一次可以被编译器优化掉)
2.使用=号赋值的时候发生一次(这一次是避免不了的)
另外,如果你在一个类中只提供了move constructor ,但是在=号赋值,或者直接创建对象的时候并没有匹配到move constructor 这时候编译器会自动生成一个复制构造函数
在实践了很多右值引用后,还有返回右值引用的函数,我感觉右值引用最关键的一个功能就是阻止拷贝(复制)的发生。
24.c++数组的引用
void foo(T(&arr)[N])
{
bar(std::begin(arr), std::end(arr));
}
其实我早就说过,引用的引入实际上就是为了掩蔽c++中对指针的操作,使得c++更加语义化,更加natural,此例中函数传递数组的运用就是一个很好的例子