基础真的重要…
sizeof
根据以下条件进行计算:
1、 结构体的大小等于结构体内最大成员大小的整数倍
2、 结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说double型成员相对于结构体的首地址的地址偏移量应该是8的倍数。
拓展:
空的类也是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。
1)类内部的成员变量:
普遍的变量:是要占用内存的,但是要注意内存对齐(这点和struct类型很相似)
static修饰的静态变量:不占用内存,原因是编译器将其放在全局变量区
2)类内部的成员函数
非虚函数(构造函数、static函数、成员函数等):不占用内存
虚函数:要占用4个字节(32操作系统),用来指定虚拟函数表的入口地址。跟虚函数的个数没有关系。父类和子类共享一个虚函数指针。
拓展:
存在父类和子类以及虚继承时,sizeof的大小:
类的大小为类的非静态数据成员的类型大小之和,静态成员数据不作考虑
空类的大小为1,单一继承和多重继承的空类空间为1,虚继承为4
构造函数、析构函数以及普通的成员函数跟sizeof无关
带有虚函数的类,因为要维护一个虚函数表所以占用一个指针的空间4
子类如果是重新实现的父类的虚函数,不计入sizeof大小
虚继承涉及到虚表(虚指针),大小为4
class A{};
class A2{};
class B:public A{};
class C:public virtual B{};
class D:public A,public A2{};
sizeof(A);//1
sizeof(B);//1
sizeof(C);//4
sizeof(D);//1
总结:
计算结构变量的大小就必须讨论数据对齐问题。为了使CPU存取的速度最快,C++在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫数据对齐
sizeof操作符不能用于函数类型、不完全类型或位字段。不完全类型指具有未知存储大小数据的数据类型,如未存储大小的数组类型、未知内容的结构或联合类型、void类型等。
this指针
1)this指针只能在成员函数中使用
全局函数 ,静态函数不能使用this.
成员函数默认第一个参数为:T *const this
eg:class A{public: int func(int p) { }};
在编译期看来:int fun(A* const this,int p);
2)由此可见,this 在成员函数的开始前构造,在成员函数的结束后清除。
这个生命周期同任何一个函数的参数一样,没有任何区别。
A a;
a.fun(10);
编译器将其编译为:
A::func(&a,10);
通过eax寄存器传递this指针(对象的地址)
C++11新特性:
auto
从初始化表达式中推断出变量的数据类型
在编译时对变量进行类型推导,但是不会影响编译速度,因为编译时本来也要右侧推导判断与左侧是否匹配
auto i = 1;
auto d = 1.0;
auto str = "Hello World";
auto ch = 'A';
auto func = less<int>();
vector<int> iv;
auto ite = iv.begin();
cout<<"i:"<<typeid(i).name()<<endl;
cout<<"d:"<<typeid(d).name()<<endl;
cout<<"str:"<<typeid(str).name()<<endl;
cout<<"ch:"<<typeid(ch).name()<<endl;
cout<<"func:"<<typeid(func).name()<<endl;
cout<<"ite:"<<typeid(ite).name()<<endl;
decltype
decltype实际上有点像auto的反函数,auto可以让你声明一个变量,而decltype则可以从一个变量或者表达式中得到类型,
int x = 3;
decltype(x) y = x;
cout<<"y:"<<typeid(y).name()<<endl;//int
nullptr
nullptr是为了解决C++中NULL的二义性问题而引进的一种新的类型,因为NULL实际上代表的是0。
void F(int a)
{
cout<<a<<endl;
}
void F(int *p)
{
assert(p != NULL);
cout<<p<<endl;
}
int main()
{
int *p = nullptr;
int *q = NULL;
bool equal = (p == q);// ==
cout<<equal<<endl;
//int a = nullptr;编译失败 nullptr不能转化为int
int a = NULL; //#define NULL 0
F(0);//在C++98中编译失败,有二义性;在C++11中调用F(int)
F(nullptr);
return 0;
}
序列for循环
在C++中for循环可以使用类似java简化的for循环.可以用于遍历数组,容器,string以及由begin和end函数定义的序列(即有iterator),示例代码如下:
void main()
{
int arr[] = {9,5,7,2,4,2,9,4,4};
vector<int> vec(arr,arr+9);
for(auto x : vec)
{
cout<<x<<" ";
}
cout<<endl;
}
变长参数模板
我们在C++中都用过pair,pair可以使用make_pair构造,构造一个包含两种不同类型的数据的容器,比如:
auto p = make_pair(1,"gyz Hello!");
由于C++11中引入了变长参数模板,故引入了新的数据类型:tuple,tuple是一个N元组,可以传入1个,2个甚至多个不同类型的数据
auto t1 = make_tuple(1,2.0,"gyz");
auto t2 = make_tuple(1,2.0,"gyz",{1,2,3});
这样做避免了从前pair中嵌套pair的丑陋做法,使得代码简洁
另一个经常见到的例子是Print函数,在C语言中printf可以传入多个参数,在C++11中,我们可以用变长参数模板实现更简洁的Print
template<typename head,typename... tail>
void Print(Head head,typename... tail)
{
cout<<head<<endl;
Print(tail...);
}
Print中也可以传入多个不同种类的参数,如下:
Print(1,1.0,"gyz Hello!");
更加优雅的初始化方法
在引入C++11之前,只有数组能使用初始化列表,其他容器想要使用初始化列表,只能用以下方法:
int arr[] = {9,5,7,2,4,2,9,4,4};
vector<int> vec(arr,arr+9);
在C++11中,可以使用以下语法进行替换:
int arr[3]{1,2,3};
vector<int> vec{1,2,3};
map<int,string>{{1,"a"},{2,"b"}};
string str{"Hello World"};
最新版编译器能够支持的C++11特性将会更多…
另外算法
动态规划
详见后期博文…