这两天对于指针,类的使用有多一点的发现,故把一些发现写下来,对一些知识点的总结,排版可能不是那么合理,就只是为了以后自己忘了可以看看,毕竟好记性不如烂笔头嘛,哈哈哈哈。
1、类声明指针的函数调用
1、类的函数无论是静态还是非静态函数都是存放在代码区,所以可以理解为类的函数和每个我们在堆或栈中生成的对象是独立(独立指的是不在同一块内存区),对象在堆或栈内存中存在的空间只是成员变量和虚函数表(也许还有其他的,这里没有说明,虚函数表是一个指针指向)累加的空间。当对象去调用对象的成员函数时,就是调用的代码区中对应的类的函数,只是如果调用的函数需要操作到该类的成员数据,会传递this进去,这样就可以获取到对应的对象不同的数据了。
2、指针调用的函数是声明指针的类的函数,和后面的new 的对象无关(这里的无关是指并非调用new的对象的函数)
#include <iostream>
using namespace std;
class C {
public:
C()
{
cout << "C::C()" << endl;
c = 'a';
}
void print()
{
cout << "C::print()" << endl;
cout << "C::c = " << c << endl;
}
private:
char c;
};
class A
{
public:
A()
{
a1 = 1;
cout << "A::A()" << endl;
}
void print()
{
cout << "A::print()" << endl;
cout << "A::a1 = " << a1 << endl;
}
private:
int a1;
};
main.cpp
int main()
{
char* pcC = (char*)new C(); //1
A *pA = (A*)pcC;
pA->print(); //2
cout << "--------------------" << endl;
C *pC = (C*)pcC;
pC->print(); //3
delete pC;
return 0;
}
输出结果:
C::C()
A::print()
A::a1 = 97
--------------------
C::print()
C::c = a
(1)main函数中//1 代码在堆中开辟了C类的对象,内存中开辟了1字节,因为C类中只有一个成员char,所以是一个字节(当有其他类型的成员变量时,才会进行字节对齐。)
(2)注释//2指针pA调用了A类的函数print(),同时pA指针指向的地址是C类对象的空间,因为C类的对象的堆空间中数据是一个字符‘a’,则pA所指向的地址中的数据就是'a',所以转化为int输出的就是97(输出什么类型只是你想让程序表达的方式而已,在内存中无论是字符,还是int等其他类型数据,其根本都是0101组合的数据)
(3)注释//3调用了C类的函数print()
再举个例子
//类A的print()不涉及到该类的成员数据,则不涉及需要传递this指针
class A
{
public:
A()
{
a1 = 1;
cout << "A::A()" << endl;
}
void print()
{
cout << "A::print()" << endl;
}
private:
int a1;
int a2;
int a3;
int a4;
};
int main()
{
A* pA = NULL;
pA->print();
return 0;
}
输出结果:
A::print()
在VS上编译会出错,但是在Linux上面的输出是A::print(),所以也验证了跟函数是独立放在代码区。
总结:
1、指针变量占用4个字节(32位系统),8个字节(64位),因为指针变量的值是内存地址,内存地址是根据一个字长的长度所以和系统的位数有关。
2、声明指针的类型只是告诉编译器,这个指针指向的内存空间可以取多大的空间,比如声明E *pE,则告诉编译器pE这个指针可以取4×3=12个字节,在pE指向的内存地址(4字节)接下来的12个字节都属于这个指针的访问范围。同时也是告诉编译器指针的步长跨越的字节长度。比如定义E aE[4]; 输出每个元素的地址,0x7ffd42b68a20 0x7ffd42b68a2c,第一个元素和第二个元素相差12个字节。
3、指针的步长递增取数据,比如aE + 1进行取数据,仅限于内存空间连续,因为指针的往前+1取值要根据声明的类型来进行计算往前跳的步长,再次显现了声明类对与指针取值的作用了。假设不连续的话,那取出来的数据就是不确定的了,还可能非法访问了,毕竟有些内存地址可能不是这个进程所能访问的。
class E
{
private:
int e1;
int e2;
int e3;
}
所以自己之前和链表有所混淆了,后来想想链表是需要开辟一个空间去存下一个节点的地址,取数据也不是简单的+1,而是取出当前节点保存的下个节点的地址,所以内存可以不连续,但是单纯的使用指针+1的话,之前的想法是+1就能获取到下个对象的指针。但是实际并不能做到如此智能,所以之前自己是有点混淆了。把链表的模型给套接在了所有指针访问的方式,没有经过仔细的思考,进行加以区分使用加深印象。现在才有了一些概念。
2、父类子类同名变量
class BaseD
{
public:
BaseD()
{
cout << "BaseD::BaseD()" << endl;
d = 0;
}
void printdAddr()
{
cout << "BaseD::printdAddr() &d = " << &d << endl;
}
void print()
{
cout << "BaseD::print()" << endl;
}
int d;
private:
int m_dPrivate;
};
class D : public BaseD
{
public:
D()
{
cout << "D::D()" << endl;
d = 1;
}
void printdAddr()
{
cout << "D::printdAddr() &d = " << &d << endl;
}
void print()
{
cout << "D::print()" << endl;
}
int d;
};
int main()
{
BaseD *baseD = new D();
cout << "d = " << baseD->d << " &d = " << &(baseD->d) << endl;
cout << "sizeof BaseD = " << sizeof(BaseD) << endl;
baseD->printdAddr();
baseD->print();
cout << "---------------------" << endl;
D *d = (D *)baseD;
cout << "d = " << d->BaseD::d << " &d = " << &(d->BaseD::d) << endl;
cout << "---------------------" << endl;
D *pD = new D();
cout << "sizeof D = " << sizeof(D) <<endl;
pD->printdAddr();
pD->print();
delete d;
delete pD;
}
子类和父类的同名变量/函数,在如果默认的调用方式是 指针变量->成员变量 则调用的是声明类的成员变量例如下面baseD->d,调用的是BaseD::d=0,而pD 调用的是D::d=1;如果要调用D::d, 必须是子类的指针D* d指向子类的对象,例如第一种情况父类指针BaseD *baseD虽然指向了D对象,但是无法输出D::d;如果子类对象要调用父类BaseD::d,则加上pD->BaseD::d范围符号则可以输出,父类的成员
3、初始化
1、
int main()
{
int* iA = new int(4);
cout << "*(iA) = " << *iA << endl;
cout << "*(iA + 10) = " << *(iA + 10) << endl;
}
Linux输出结果是:
*(iA) = 4
*(iA + 10) = 0
2、
int main()
{
A aa;
aa.print();
A *pa = new A();
pa->print();
delete pa;
return 0;
}
Linux输出结果:
A::A()
A::print()
A::a1 = 1
A::a2 = 0
A::a3 = 0
A::a4 = 0
A::A()
A::print()
A::a1 = 1
A::a2 = 0
A::a3 = 0
A::a4 = 0
所以Linux很多情况都是不确定的情况下输出0?因为理论上应该是输出随机数才对,因为在VS上输出的随机数。这个暂时还没找到文档确认这个机制是如何的。留下问题,以后找到权威的解答再回答。