【C++】C++中的Sequence Point,构造函数等问题

本文探讨了C++中的SequencePoint概念、构造函数为何无返回值、如何显式调用构造及析构函数、拷贝构造函数为何需引用传递,以及static与const不能连用于类成员函数的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文将简要的讨论以下几个问题

  • 什么是Sequence Point
  • C++的构造函数为什么没有返回值
  • 如何显示调用构造函数和析构函数
  • 拷贝构造函数为什么必须是引用传递
  • 类成员函数中static和const 不能连用的原因

1. 什么是Sequence Point

在现今的笔试中 ,我们经常会遇到如下的问题,当下列语句执行完后,a的值是多少?

int a = 1; // no side effect
a = a ++;  // side effect

答案无非是提供了,1, 2, undefined…
其实上述例子,在C++中已经有相应的名词Sequence Point

A sequence point is a point in the program’s exexution sequence where all previous side-effects shall have take place and where all subsequent side-effects shall not have take place.

在C++中,表达式计算存在两种类型,一是无副作用的,而是有副作用(side effect)的。 如上述代码所示。
而一个 sequence point 按照上述定义为,该点前的表达式的所有副作用,在程序执行到达改点之前发生完毕;该点后的表达式的所有副作用,在程序执行到达该点时尚未发生。

  • 法则1 在表达式求值时,在前一个和下一个顺序点之中,一个对象所存储的值至多只能被修改一次。如下图的结果是undefined
    rule 1
  • 法则2 在表达式求值的过程中会更改某个对象的值,要求更改前的值被读取的唯一目的是用来确定存入新值。
    rule 2

在C中,规定的Sequence Point很少,因为这有益于编译器的优化最大化。


2. C++的构造函数为什么没有返回值

上一个简单的例子,帮助理解与记忆。

class Base {
// ...
};

void foo(int a) {
    // do something... 
}

void foo(const Base& base) {
    // do something...
}

int main()
{
    // 如果构造函数返回值,下面的结果将调用哪个函数?
    foo(Base()); 
    return 0;
}

很显然,构造函数不设定返回值,是因为构造函数的特殊性质决定的。如果有返回值,那么将会存在很多问题。


3. 如何显示调用构造函数和析构函数

事实上,我们采用new操作符时,一般会发生以下三件事情:

  • 调用::operator new 分配所需内存
  • 调用对象的构造函数
  • 返回新分配的并构造的对象的指针

我们用以下代码来模拟这个过程。结果说明,我们总是可是显示的调用构造函数和析构函数。

class Base
{
public:
    Base() { cout << "constructors" << endl; }
    ~Base() { cout << "Destructors" << endl; }
};

int main()
{
    Base *pb = (Base *)malloc(sizeof(Base));
    new(pb) Base();
    pb->~Base();
    delete pb;

    return 0;
}

4. 拷贝构造函数为什么必须是引用传递

在深入探讨之前,首先列出拷贝初始化的几种情况:

  • 在使用=定义变量时, 如 Foo newObj = oldObj
  • 将 一个对象作为实参传递给一个非引用类型的实参;
  • 从一个返回类型为非引用类型的函数返回一个对象;
  • 用花括号初始化一个数组中的元素或一个聚合类中的成员;
  • 某些类类型会对他们所分配的对象使用拷贝初始化。例如,当我们初始化标准库容器或者调用insert或push成员,容器会对其进行拷贝初始化。而用emplace成员创建的元素则都进行直接初始化。
class Base
{
public:
    Base() { cout << "constructors" << endl; }
    Base(const Base bs) {
        a = bs.num;
    }
    ~Base() { cout << "Destructors" << endl; }

private:
    int num;
};

正如上述所说的,在函数调用过程中,具有非引用类型的参数进行拷贝初始化。类似的,当一个具有非引用类型的返回类型时,返回值会被用来初始化调用方的结果。
拷贝构造函数被用来初始化非引用类类型参数,如果参数不是引用类型,则将进入死循环–>因为为了调用拷贝构造函数,必须拷贝它的实参,但为了拷贝实参,我们又需要调用拷贝构造函数,如此无限循环


5. 类成员函数中static和const 不能连用的原因

C++的设计准则之一:nonstatic member function必须至少和nonmember function有相同的效率。
例如:

float magnitude3d(const Point3d *_this) { //... }
float Point3d::magnitude3d() const { //... }

对于后者的nonstatic member function而言,其最终会扩展为:

Point3d Point3d::magnitude(const Point3d *const this) {
    //...
}

由上可见,const修饰的是指针this,而this属于object的范畴。而对于static而言,类中的static成员函数或成员变量,不隶属于某个具体的object,其范畴是整个class。因此,修饰的范畴不同,故static和const在类中对同一个成员函数或者成员变量不能一起使用


### 单冒号 `:` 的作用和用法 在 C++ 中,单冒号 `:` 是一种语法符号,在不同的上下文中具有多种含义。以下是其主要用途及其解释: #### 1. 条件运算符的一部分 `?:` 被称为三元条件运算符,用于根据布尔表达式的值返回两个可能的结果之一。 例如: ```cpp int result = (a > b) ? a : b; ``` 在此处,如果 `(a > b)` 成立,则返回 `a`;否则返回 `b`[^5]。 #### 2. 基类初始化列表中的分隔符 当定义构造函数时,可以使用成员初始化列表来初始化基类或数据成员。此时,`: ` 符号用来分隔构造函数名与其后的初始化列表。 例如: ```cpp class Base { public: int value; Base(int v) : value(v) {} // 使用冒号分隔构造函数名称与初始化列表 }; ``` #### 3. 类型限定符 在命名空间或嵌套结构体/类中,`::` 双冒号操作符表示范围解析操作符,而单冒号通常作为双冒号的前缀部分出现。尽管严格来说这不是单独使用的冒号功能,但在某些情况下会看到类似的写法。 例如: ```cpp namespace Example { class MyClass {}; } Example::MyClass obj; // 此处 :: 表达完整的范围解析概念 ``` #### 4. 标签语句 在控制流转移指令(如 goto 或 switch-case 结构)里,`:` 定义了一个标签位置供程序跳转至该执行后续逻辑。 ```cpp goto label_name; label_name: { /* Code here */ } // or within switches... switch(variable){ case constant_value: statement_sequence break; default : default_statement ; } ``` 综上所述,虽然没有直接提及于所提供的参考资料之中,但是通过分析得知上述四种情况涵盖了大部分关于C++编程语言里面single colon的实际应用场景以及它们各自代表的意义[^6]。 ```cpp #include <iostream> using namespace std; struct Point { double x,y; // Constructor with initializer list using ':' Point(double xx,double yy):x(xx),y(yy){} }; int main(){ bool condition=true; cout << ((condition)? "True":"False")<<endl;//Ternary Operator Usage Point p(3.0,4.0); cout<<"Point coordinates:"<<p.x<<","<<p.y<<".\n"; return 0;} ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值