C++类、指针和内存的见解

1 篇文章 0 订阅
1 篇文章 0 订阅

这两天对于指针,类的使用有多一点的发现,故把一些发现写下来,对一些知识点的总结,排版可能不是那么合理,就只是为了以后自己忘了可以看看,毕竟好记性不如烂笔头嘛,哈哈哈哈。

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上输出的随机数。这个暂时还没找到文档确认这个机制是如何的。留下问题,以后找到权威的解答再回答。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值