C++学习中踩过的一些坑和错题记录

内联函数的主要作用是提高程序的执行效率,可以减少函数调用的开销,但同时会增加代码的体积。内联函数的适用场景一般包括以下两种情况:

函数代码简单,且在程序中被频繁调用。这种情况下,内联函数可以减少函数调用的开销,提高程序的执行效率。
函数代码复杂,但在程序中只被少量调用。这种情况下,内联函数可以减少函数调用的开销,但同时会增加代码的体积。如果函数代码过于复杂,可能会导致内联函数的代码体积过大,从而反而降低程序的执行效率。

2.C++ 内存布局可以分为以下几个部分:

代码段(Code Segment):存储程序的指令代码,通常是只读的。在可执行文件加载到内存时,代码段会被映射到进程的虚拟地址空间中,并被标记为只读。

全局数据段(Global Data Segment):存储程序中全局变量和静态变量的值。在可执行文件加载到内存时,全局数据段也会被映射到进程的虚拟地址空间中。

堆(Heap):动态分配的内存空间,通常由程序员手动管理。在程序运行期间,堆的大小可以动态增长或缩小。

栈(Stack):存储函数调用过程中的临时数据,例如局部变量、函数参数、返回值等。栈的大小通常是固定的,由系统分配。

其他(例如程序自身需要用到的数据区):通常是一些特殊用途的内存区域,例如程序需要用到的共享内存、信号量等。

C++ 内存布局中,栈和堆是两个非常重要的内存区域。栈由系统自动管理,每次函数调用时都会在栈上为函数的局部变量和参数分配一段内存空间,并在函数返回时自动释放这些内存空间。堆则是由程序员手动管理,通常使用 new/delete 或 malloc/free 等函数来动态分配和释放内存空间。由于堆的动态性,所以容易出现内存泄漏和内存泄露等问题,因此在使用堆时需要格外注意内存管理。

写出以下程序运行的结果。( )

class Sample 
{
public:
      Sample();
      void Display();
private:
      int i;
      static int k;
};

Sample::Sample() 
{
	i=0;
	k++;
}

void Sample::Display() 
{
   cout << "i=" << i << ",k=" << k << endl;
}

int Sample::k=0;

int main( ) 
{
    Sample a, b;
    a.Display();
    b.Display();
    
    return 0;
}

运行结果:

i=0,k=2
i=0,k=2

构造函数是一种特殊的成员函数,用于初始化类的对象。在 C++ 中,构造函数的名称必须与类的名称相同,没有返回类型,不接受任何参数(或者接受一个或多个参数),并在对象创建时自动调用。

在上面的代码中,Sample 类的构造函数是以下代码部分:

Sample::Sample()
{
    i=0;
    k++;
}

这个构造函数没有参数,它会在创建 Sample 类对象时自动调用。在这个构造函数中,将非静态成员变量 i 的值初始化为 0,并将静态成员变量 k 的值加 1。由于 Sample 类中只定义了一个构造函数,且没有显式地初始化 i 和 k 的值,因此在创建 Sample 类对象时,i 的值会被初始化为 0,而 k 的值会被自动初始化为 0。

构造函数的作用是初始化对象的状态,使其符合设计要求。在构造函数中,可以初始化对象的数据成员,为其分配内存空间等。如果没有定义构造函数,则会使用默认构造函数,也就是编译器自动生成的构造函数,该构造函数不进行任何操作,只是为对象分配内存空间,并将对象的数据成员的值设置为 0 或 null 等默认值。

设已经有A,B,C,D 4个类的定义,程序中A,B,C,D析构函数调用顺序为?

C c;
void main()
{
A *pa=new A();
B b;
static D d;
delete pa;
}

在这个程序中,A、B、C、D 四个类的析构函数调用顺序为:B 的析构函数、A 的析构函数、D 的析构函数、C 的析构函数。

在主函数中,首先创建了一个 C 类的对象 c。由于 c 对象是一个栈对象,因此当程序执行完毕时会自动调用 c 的析构函数。由于程序中只创建了一个 c 对象,因此 C 类的析构函数调用一次。

接着,使用 new 运算符在堆上创建了一个 A 类的对象 pa,并将其指针保存在指针变量 pa 中。由于 pa 是一个指针变量,因此 pa 的销毁并不会调用 A 类的析构函数。需要在程序结束前手动使用 delete 运算符释放 pa 指向的内存,这时会调用 A 类的析构函数,先销毁 pa 指向的 A 类对象。

然后,创建了一个 B 类的对象 b。由于 b 是一个栈对象,当程序执行完毕时会自动调用 b 的析构函数。由于程序中只创建了一个 b 对象,因此 B 类的析构函数调用一次。

再然后,创建了一个静态的 D 类对象 d。由于 d 是一个静态对象,它的生命周期与程序的运行时间相同,在程序结束时会自动调用 d 的析构函数。

最后,在主函数结束时,会自动销毁栈对象 b 和 C 类对象 c,它们的析构函数调用顺序与它们的创建顺序相反,即先调用 b 的析构函数,再调用 c 的析构函数。因此,B 的析构函数会先于 A 的析构函数被调用,D 的析构函数会晚于 A 和 B 的析构函数被调用,C 的析构函数会最后被调用。

 #include <iostream>
  using namespace std;
  int i=1;
  class Test
  {
  public:
      Test()
    :_fourth(_third)
    ,_second(i++)
      ,_first(i++)
      ,_third(i++)
      {
          _third=i;
      }
      void print()                                                                        
      {
          std::cout<<"result: "<<_first + _second + _third + _fourth <<std::endl;
      }
  private:
      int _first;
      int _second;
      int _third;
      int &_fourth;
  };
  int main()
  {
      Test test;
      test.print();
      return 0;
  }
Test()
:_fourth(_third)
,_second(i++)
,_first(i++)
,_third(i++)
{
    _third=i;
}

这段代码是 Test 类的构造函数,它用于初始化 Test 类的对象。在这个构造函数中,通过初始化列表对 Test 类的数据成员进行了初始化,包括:

_fourth(_third):使用引用成员 _fourth 来初始化 _third,即让 _fourth 成为 _third 的别名,它们两个变量引用同一个内存地址。
_second(i++):使用 i 的值初始化 _second,然后将 i 的值加 1。
_first(i++):使用 i 的值初始化 _first,然后将 i 的值加 1。
_third(i++):使用 i 的值初始化 _third,然后将 i 的值加 1。
_third=i;:再次将 i 的值赋给 _third,此时 _third 的值为 4。
因此,当创建一个 Test 类的对象时,它的数据成员的初始值分别为:_first = 2,_second = 1,_third = 4,_fourth = 4。在构造函数中,可以在初始化列表中为对象的数据成员赋值,而不必在构造函数的函数体中进行赋值操作,这样可以提高代码的效率和可读性。

下列代码在编译时会产生错误的是()

#include <iostream>

using std::cout;
using std::endl;

struct Foo
{
	Foo()
	{
	}
	

	Foo(int)
	{
	}
	
	void fun()
	{
	}

};

int main(void)
{
	Foo a(10);//语句1
	a.fun();//语句2
	Foo b();//语句3
	b.fun();//语句4 
	return 0;
}

错误代码:

b.fun();//语句4

在代码中,对于对象 b 的初始化写法是 Foo b();,看起来似乎是在调用默认构造函数创建对象 b,然后调用它的成员函数 fun()。但实际上,这样的写法是一个函数声明,而不是创建对象的语句,它声明了一个函数名为 b,返回类型为 Foo 的函数,该函数不带参数。

因此,当执行 b.fun(); 时,编译器会认为这是在调用函数 b,并期望 b 返回一个 Foo 类型的对象,进而调用返回类型为 Foo 的函数的成员函数 fun()。但由于 b 并不是一个对象,而是一个函数名,因此会出现编译错误。

要想创建一个对象 b 并调用它的成员函数 fun(),可以改为如下写法:

Foo b;
b.fun();

或者将其合并成一行:

Foo b; b.fun();

这样就可以正常创建对象并调用其成员函数了。

7.以下关于this指针的叙述中正确的是( )

附件:
暂无
A
任何与类相关的函数都有this指针
B
类的成员函数都有this指针
C
类的友元函数都有this指针
D
类的非静态成员函数才有this指针

D。this指针是C++语言中的一个关键字,它是一个指向当前对象的指针。在类的非静态成员函数中,编译器会自动为函数添加一个this指针,指向当前对象的地址,从而可以在函数中访问该对象的成员变量和成员函数。

因此,正确的叙述是:类的非静态成员函数才有this指针。选项A、B和C都不准确。虽然大多数与类相关的函数都有this指针,但这些函数不一定是类的成员函数,例如全局函数或静态成员函数都可以与类相关。而类的友元函数也不一定有this指针,因为友元函数可以是全局函数或其他类的成员函数。

this指针存在的目的是( )

附件:
暂无
A
保证基类公有成员在子类中可以被访问
B
保证每个对象拥有自己的数据成员,但共享处理这些数据成员的代码
C
保证基类保护成员在子类中可以被访问
D
保证基类私有成员在子类中可以被访问

B。this指针是C++语言中的一个关键字,它是一个指向当前对象的指针。在类的非静态成员函数中,编译器会自动为函数添加一个this指针,指向当前对象的地址,从而可以在函数中访问该对象的成员变量和成员函数。

因此,this指针的存在目的是为了保证每个对象拥有自己的数据成员,但共享处理这些数据成员的代码。this指针让每个对象的成员函数在运行时都可以访问到自己所属的对象,从而实现对象的数据共享和数据封装。

选项A、C和D中的访问权限不准确。在C++中,基类的公有、保护和私有成员在派生类中的访问权限是由派生类的访问修饰符来控制的,与this指针的作用无关。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值