1、
#include<iostream>
using namespace std;
void foobar(int a,int*b,int **c) {
int *p=&a;
*p=101;
*c=b;
b=p;
}
int main()
{
int a=1;
int b=2;
int c=3;
int *p=&c;
foobar(a,&b,&p);
cout<<a<<""<<b<<" "<<c<<""<<*p<<endl;
return 0;
}
注意:改变一个数据需要一级指针,改变一个一级指针需要二级指针,引用就是一个别名;
函数foobar中的a是按值传递,因此在函数中的修改不会引起主函数中的变化。
函数中b传递的是主函数中b的指针,语句b = p ,其中p指向的是函数foobar内局部变量a的地址,让传递过去的指针换了指向的数据,原来指向的数据(主函数中的b)不会有影响。如果这里是*b = *p那么主函数中的b也要相应变化。
函数中的c传递的是双重指针,*c = b,也就是让主函数中的p指针指向了主函数中的b的地址在函数foobar中对指针的变化没有影响到主函数,只是让双重指针更换了指向而已。
说了半天上个图吧,比较直接。说明:值用=表示,指针用:表示,双重指针用::
2、
#include<iostream>
#include<string.h>
using namespace std;
class A {
};
class B{
char ch;
int x;
};
class C{
public:
void Print(void) {}
};
class D {
public:
virtual void Print(void) {
}
};
int main()
{
cout<<sizeof(A)<<""<<sizeof(B)<<" "<<sizeof(C)<<""<<sizeof(D)<<endl;
return 0;
}
分析:类A空类型的实例虽然不包含任何信息,但是必须在内存中占一定的空间,否则无法使用这些实例,一般都是1;
类B因为内存对齐所以为8,
类C里面虽然有函数,但是只需要知道函数的地址即可,而这些函数的地址只与类型相关,而与类型的实例无关,编译器不会因为函数而在内存中多添加任何的额外信息.所以还是1;
类D因有虚函数,C++的编译器一旦发现一个类型中有虚函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针.因为多了一个指针,所以在32位机器为4,64位机器为8
注意:空类编译器会将sizeof()的值变为1;
类的大小只与非静态成员和虚函数的大小有关,而与其他普通函数成员无关,与构造函数析构函数也无关。
3、设p是指针变量,语句p=NULL;等价于 p=0;
分析:‘\0’的ASCII码为0,所以NULL等效于0;
4、有函数定义:
void test(int a) {}
void test (float a) {}
则以下调用错误的:D
A test(1); Btest(‘c’); C test(2+’d’); D test(0.5);
分析:编译器会提示参数不匹配,但是不匹配的原因是:类型转换只能从下往上自动转换,而不能自动从上往下转换。
0.5默认是double类型,既不能转换成float类型,也不能转换成int类型。故会提示参数不匹配。
5、若有定义
Typedef char T[10];
T * a;
等价于 char (*a)[10];
A char a[10]; B char (*a)[10]; Cchar *a; D char *a[10];
分析:a到底是指向一个10个数据的指针,还是一个指向10个指针的数组;
我认为偏向前者
看分析:
T b;相当于 char b[10];
相信大家对这个没啥意见吧
那么,T *a;也就是一个指针a指向了一片空间,空间连续,并且以T为基本单位;
来看D答案 : char *a [ 10 ]
里面的10指的是10个T类型的指针,
而typedef char T[10] 里面的10指的是10个char数值
两个10之间没啥关系;
T *a也没有告诉我们就必须申请10个指向T类型的指针
T *a只是告诉我们声明了一个a指针,指向一片地方,这片地方都是T类型的数据而已
这就像是int *a = new int[],
我们有一个指针a,以int类型为单位移动,int默认4字节移动(32位cpu)
只不过这里T是默认10个字节移动
B答案: char ( *a) [ 10 ] ; (大写A改成小写)
声明了一个指针,指针每次移动都是以T为单位的 ,符合!
***********************
// edition 1
typedef char T[10];
int main() {
T *a;
char (*b)[10];
a = b;
}
************************
// edition 2
typedef char T[10];
int main() {
T *a;
char *b[10];
a = b;
}
6、对于派生类的构造函数,在定义对象时构造函数的执行顺序:
基类构造函数-> 成员对象构造函数->派生类本身的构造函数。
分析:1).类的构造函数可能使用类的对象成员,因此类的对象成员→类的构造函数
2).派生类的构造函数可能使用父类的对象成员,因此基类的构造函数→派生类的构造函数
3).析构和构造刚好相反~
补充:
当派生类中不含对象成员时
·在创建派生类对象时,构造函数的执行顺序是:基类的构造函数→派生类的构造函数;
·在撤消派生类对象时,析构函数的执行顺序是:派生类的构造函数→基类的构造函数。
当派生类中含有对象成员时
·在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;
·在撤消派生类对象时,析构函数的执行顺序:派生类的构造函数→对象成员的构造函数→基类的构造函数。
7、公用(public):访问权限最高;除派生类外,外部函数也可以直接访问(无论是成员变量还是成员函数)。
私有(private):访问权限最低;只能是该类内部函数互相调用,派生类、外部函数都不能访问。
保护(protect):访问权限中间;该类内部函数、派生类都能访问,外部类、外部函数不能访问
我们这样来记会更加容易一些,在继承时:
1、不管采用哪种形式(public, protected或private),基类中的私有成员都不可以被继承;如果非要在派生类中使用基类的私有成员,可以有两种方法:一是使用属性,二是使用友元类或友元函数。
2、如果采用public形式,则基类除了私有成员的其它所有都被原样的继承到派生类中;即在基类中是public的,在派生类中还是public的,在基类中是protected的,在派生类中还是protected的。
3、如果采用protected形式,则基类中除了私有成员的其它说有都被以protected的形式继承到派生类中。
C++中的继承方式有:
public、private、protected三种(它们直接影响到派生类的成员、及其对象对基类成员访问的规则)。
(1)public(公有继承):继承时保持基类中各成员属性不变,并且基类中private成员被隐藏。派生类的成员只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象只能访问基类中的public成员。
(2)private(私有继承):继承时基类中各成员属性均变为private,并且基类中private成员被隐藏。派生类的成员也只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象不能访问基类中的任何的成员。
(3)protected(保护性继承):继承时基类中各成员属性均变为protected,并且基类中private成员被隐藏。派生类的成员只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象不能访问基类中的任何的成员。
8、
#define INT_PTR int;
INT_PTR a,b;
在进行替换的时候,是这个样子:
int* a,b;
也就是说a是指针类型,而b是整型。
typedef int*int_ptr;这是合适的类型定义。定义一种类型的别名,而不是简单的宏替换,还可以用在结构体的定义中。