1.交换a、b的值
//交换a,b的值
#include<stdio.h>
void swap1(int& a, int& b)
{
int temp = a; //使用局部变量temp完成交换
a = b;
b = temp;
printf("a=%d, b=%d\n",a,b);
}
void swap2(int& a, int& b)
{
a = a + b; //使用加减运算完成交换,运算时可能溢出会导致数据
b = a - b;
a = a - b;
printf("a=%d, b=%d\n",a,b);
}
void swap3(int& a, int& b)
{
a = a^b; //a=a^b;
b = a^b; //b=a^b=a^b^b=a^0=a;
a = a^b; //a=a^b^a=b^0=b
printf("a=%d, b=%d\n",a,b);
/*
使用异或运算完成交换,原理:1.x^x=0, 2.x^0=x, 3.具有结合性,可以交换位置
当a=b时此方法不适用。本方法为这三种方法中最优的。
*/
}
int main()
{
int a1 = 1, b1 = 2;
int a2 = 1, b2 = 2;
int a3 = 1, b3 = 2;
swap1(a1,b1);
swap2(a2,b2);
swap3(a3,b3);
return 0;
}
a=2, b=1
a=2, b=1
a=2, b=1
2.宏定义的使用
//宏定义的使用
//1.用#define实现宏并求最大值和最小值
#include<stdio.h>
#define MAX(x,y) (((x)>(y))?(x):(y))
#define MIN(x,y) (((x)<(y))?(x):(y))
//使用#define实现宏并求输入参数的平方
#define SQR(x) ((x)*(x))
int main()
{
int a = 1, b = 2;
int min, max, aa;
min = MIN(a, b);
max = MAX(a,b);
aa = SQR(b);
printf("min=%d\nmax=%d\n", min,max);
printf("%d*%d=%d\n",b,b, aa);
return 0;
}
min=1
max=2
2*2=4
3.const的使用
找出程序中的错误
//const的使用。找出程序中的错误
#include<stdio.h>
int main()
{
const int x = 1;
int b = 10;
int c = 20;
const int* a1 = &b;
int* const a2 = &b;
const int* const a3 = &b;
x = 2; //错误,变量x是整形常量,因此不能改变x的值
a1 = &c; //正确,const在int*的左侧,它是用修饰指针所指向的常量,即指针指向常量。
//因此,这一句把a1指向变量c是允许的,因为修改的是指针a1本身,而不是指针指向的那个值
*a1 = 1; //错误,改变a1指向的内容是不允许的
a2 = &c; //错误,const在int*的右侧,它是用来修饰指针本身的,即指针本身为常量。
//那么修改指针a2是不允许的
*a2 = 1; //正确,修改a2指向的内容是允许的。
a3 = &c; //错误,a3有两个const,表示不仅指针不能修改,并且其指向的内容也不能修改
*a3 = 1; //错误
return 0;
}
4.C++中const的作用
(1).const用于定义常量
(2).const修饰函数形式参数
当输入参数为用户自定义类型和抽象数据类型时,应该将“值传递”改为“const&传递”,可以提高效率
void fun(A a);
void fun(A const &a)
第一个效率低,函数体内产生A类型的临时对象用于复制参数a,临时对象的构造、复制、析构过程都将
消耗时间。而第二个函数提高了效率,用“引用传递”不需要产生临时对象,节省了临时对象的构造、复制、
析构过程消耗的时间。但光引用有可能改变a,所以加const
(3).const修饰函数的返回值
如给“指针传递”的函数返回值加const,则返回值不能被直接修改,且该返回值只能被赋值给加const修饰
的同类型指针。
const char* GetChar(void){};
char *ch=GetChar(); //错误
const char *ch=GetChar(); //正确
(4).const修饰类的成员函数(函数定义体)
任何不会修改数据成员的函数都应用const修改,这样,当不小心修改了数据成员或调用了非const成员函数
时,编译器都会报错。const修饰类的成员函数形式为:
int GetCount(void) const;
7.C++类的静态成员示例
//#include<stdio.h>
#include<iostream>
using namespace::std;
class widget
{
public:
widget()
{
count++;
}
~widget()
{
--count;
}
static int num()
{
return count;
}
private:
static int count;
};
int widget::count = 0;
int main()
{
widget x, y; //两个实例,输出2
cout << "The Num.is" << widget::num() << endl;
if (widget::num()>1)
{
widget x, y, z; //又定义了3个实例,输出5,if语句结束后,这3个实例销毁
cout << "The Num.is " << widget::num() << endl;
}
widget z; //又定义一个实例,输出3
cout << "The Num is" << widget::num() << endl;
return 0;
}
//类widget有一个静态成员count和一个静态方法num。我们知道,类中的静态成员或方法不属于类的实例,
//而属于类本身并在所有类的实例间共享。在调用他们时应该用类名加上操作符“::”来引用。
//类widget的构造方法里把静态成员count的值加1,在类的析构方法里把静态成员count的值减1。也就是说,
//静态成员count的值表示类widget实例的个数。
The Num.is2
The Num.is 5
The Num is3
8.使用sizeof计算普通变量所占空间大小
#include<iostream>
using namespace::std;
int main()
{
char str[] = "hello";
char *p = str;
int n = 10;
cout << "sizeof(str)= " << sizeof(str) << endl; //6,strlen("hello")+1,数组中有一个元素保存字符串结束符
cout << "sizeof(p)= " << sizeof(p) << endl; //4,在32位WinNT平台下,指针和int都是4个字节
cout << "sizeof(n)= " << sizeof(n) << endl; //4
return 0;
}
sizeof(str)= 6
sizeof(p)= 4
sizeof(n)= 4
9.使用sizeof计算类对象所占空间大小
#include<iostream>
using namespace::std;
class A
{
public:
int i;
};
class B
{
public:
char ch;
};
class C
{
public:
int i;
short j;
};
class D
{
public:
int i;
short j;
char ch;
};
class E
{
public:
int i;
int ii;
short j;
char ch;
char chr;
};
class F
{
public:
int i;
int ii;
int iii;
short j;
char ch;
char chr;
};
class S1
{
char c1;
int i;
char c2;
};
class S2
{
int i;
char c1;
char c2;
};
int main()
{
cout << "sizeof(int) = " << sizeof(int) << endl; //int占4个字节
cout<< "sizeof(short) = " << sizeof(short) << endl; //short占2个字节
cout << "sizeof(char) = " << sizeof(char) << endl; //char 占1个字节
cout << endl;
cout << "sizeof(A) = " << sizeof(A) << endl; //4
cout << "sizeof(B) = " << sizeof(B) << endl; //1
cout << "sizeof(C) = " << sizeof(C) << endl; //4+2+2(补齐)=8
cout << "sizeof(D) = " << sizeof(D) << endl; //4+2+1+1(补齐)=8
cout << "sizeof(E) = " << sizeof(E) << endl; //4+4+2+1+1=12,不需要补
cout << "sizeof(F) = " << sizeof(F) << endl; //4+4+4+2+1+1=16,不需要补
cout << "sizeof(S1) = " << sizeof(S1) << endl; // 1+3(补)+4+1+3(补)=12
cout << "sizeof(S2) = " << sizeof(S2) << endl; // 4+1+1+2(补)=8
return 0;
}
sizeof(int) = 4
sizeof(short) = 2
sizeof(char) = 1
sizeof(A) = 4
sizeof(B) = 1
sizeof(C) = 8
sizeof(D) = 8
sizeof(E) = 12
sizeof(F) = 16
sizeof(S1) = 12
sizeof(S2) = 8
10.使用sizeof计算含有虚函数的类对象的空间大小
#include<iostream>
using namespace::std;
class Base
{
public:
Base(int x) :a(x)
{
}
void print()
{
cout << "base" << endl;
}
private:
int a;
};
class Derived :public Base
{
public:
Derived(int x) :Base(x - 1), b(x)
{
}
void print()
{
cout << "derived" << endl;
}
private:
int b;
};
class A
{
public:
A(int x) :a(x)
{
}
virtual void print()
{
cout << "A" << endl;
}
private:
int a;
};
class B :public A
{
public:
B(int x) :A(x - 1), b(x)
{
}
virtual void print()
{
cout << "B" << endl;
}
private:
int b;
};
int main()
{
Base obj1(1);
cout << "size of Base obj is " << sizeof(obj1) << endl;
Derived obj2(2);
cout << "size of Derived obj is " << sizeof(obj2) << endl;
A a(1);
cout << "size of A obj is " << sizeof(a) << endl;
B b(2);
cout << "size of B obj is " << sizeof(b) << endl;
return 0;
}
//对于Base类来说,它占用内存大小为sizeof(int),等于4,print()函数不占内存。
//对于Derived类来说,比Base类多一个整型成员,因而多4个字节,一共是8个字节。
//对于A类来说,由于它含有虚函数,因此占用的内存除了一个整型变量之外,还包括
//一个隐含的虚表指针成员,一共是8个字节
//对于B类来说,比A类多一个整型成员,因而多4个字节,一共是12个字节。
//注意:普通函数不占用内存,只要有虚函数,就会占用一个指针大小的内存,原因是系统多用了
//一个指针维护这个类的虚函数表,并且注意,无论这个类中再有多少个虚函数,都不会再影响类的大小了。
size of Base obj is 4
size of Derived obj is 8
size of A obj is 8
size of B obj is 12
虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如下:
class A
class B1:public virtual A;
class B2:public virtual A;
class D:public B1,public B2;
虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,也并不常用,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要因为这样只会降低效率和占用更多的空间。
11.使用sizeof计算虚拟继承的类对象的空间大小
#include<iostream>
using namespace std;
class A
{
};
class B
{
};
class C :public A, public B
{
};
class D :virtual public A
{
};
class E :virtual public A, virtual public B
{
};
class F
{
public:
int a;
static int b;
};
int F::b = 10;
int main()
{
cout << "sizeof(A) = " << sizeof(A) << endl;
cout << "sizeof(B) = " << sizeof(B) << endl;
cout << "sizeof(C) = " << sizeof(C) << endl;
cout << "sizeof(D) = " << sizeof(D) << endl;
cout << "sizeof(E) = " << sizeof(E) << endl;
cout << "sizeof(F) = " << sizeof(F) << endl;
return 0;
}
//A类,由于A类是空类,编译器会安插一个char给空类,用来标记它的每一个对象。因此其大小为1个字节
//B类,和A类一样,也是1
//C类多重继承自A和B,其大小仍然为1个字节
//D类虚继承自A,编译器为该类安插一个指向父类的指针,指针大小为4。由于此类有了指针,编译器就不会
//安插一个char了,因此其大小是4个字节。
//E类虚继承自A并且也虚继承自B,因此它有指向父类A的指针和指向父类B的指针,加起来大小为8个字节。
//F类含有一个静态成员变量,这个静态成员的空间不在类的实例中,而是像全局变量一样在静态存储区中
//被每一个类的实例共享,因此F类的大小就是int的大小,为4个字节。
sizeof(A) = 1
sizeof(B) = 1
sizeof(C) = 1
sizeof(D) = 4
sizeof(E) = 8
sizeof(F) = 4
12.使用sizeof计算联合体的大小
#include<iostream>
using namespace std;
union u
{
double a;
int b;
};
union u2
{
char a[13];
int b;
};
union u3
{
char a[13];
char b;
};
int main()
{
cout << "sizeof(u) = " << sizeof(u) << endl;
cout << "sizeof(u2) = " << sizeof(u2) << endl;
cout << "sizeof(u3) = " << sizeof(u3) << endl;
return 0;
}
//联合体的大小取决于它所有的成员中占用空间最大的一个成员的大小。并且对于复合数据类型,
//如union、struct、class的对齐方式为成员中最大的成员对齐方式。
//对于u来说,8,大小就是最大的double类型成员a了,即sizeof(u)=sizeof(a)=8
//对于u2来说,16,最大的空间是char[13]类型的数组。这里需要注意,由于它的另一个成员int b的存在,u2的对齐方式
//变为4,也就是说,u2的大小必须在4的对齐上,所以占用的空间变为最接近13的对齐,即16
//对于u3来说,13,最大的空间是char[13]类型数组,即sizeof(u3)=13
sizeof(u) = 8
sizeof(u2) = 16
sizeof(u3) = 13
13.#pragma pack的作用
//对齐是可以更改的,使用#pragma pack(x)可以改变编译器的对齐方式
#include<iostream>
using namespace std;
#pragma pack(1) //设置对齐方式为1
struct test
{
char c;
short s1;
short s2;
int i;
};
int main()
{
cout << "sizeof(test) = " << sizeof(test) << endl; //sizeof(test)=1+2+2+4=9
return 0;
}
sizeof(test) = 9
14.为什么要引入内联函数
//引入内联函数的主要目的是,用它替代C语言中表达式形式的宏定义来解决程序中函数调用的效率问题。
15.为什么不把所有的函数都定义成内联函数
//(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
//(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
16.内联函数与宏有什么区别
//(1).内联函数在编译时展开,宏在预编译时展开
//(2).在编译的时候,内联函数可以直接被镶嵌到目标代码中,而宏只是一个简单的文本替换
//(3).内联函数可以完成诸如类型检测、语句是否正确等编译功能,宏就不具有这样的功能。
//(4).宏不是函数,inline函数是函数
//(5).宏在定义时要小心处理宏参数(一般情况是把参数用括号括起来),否则容易出现二义性。而内联函数定义时不会出现二义性