今天是学习《重学C++ 重构你的C++知识体系》 的第二天,重点在 C++基础句法。

三、C++基础句法

(一)自定义结构:枚举

enum 不仅能够创建符号变量,还能定义新的数据类型。

声明:

enum FinderWeekDayEnum{
    Monday, 
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

定义:

FinderWeekDayEnum day

(二)结构体和联合体

  1. 使用 struct 定义的是结构体:

    struct Student {

     char name[6];
     int age;
     Score s;
    

    }

  2. 使用 union 定义的是联合体:

    union Score {

     double sc;
     char level;
    

    }

  3. 结构体内存布局

(三)指向函数的指针

每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针。

一般形式: 数据类型(*指针变量名)(参数表)
举例: int(*nameFuction)(int)

(四)命名空间

定义,使用 namespace

namespace wangWorld {
    int test();
}

开始使用:

wangWorld::test()

或者

using  namespace wangWorld
test()

using namespace wangWorld 在使用上等价 import wangWorld.

一般来说,如果你使用的是class,那么class一定程度上充当了namespace的效果,class和namespace互斥。

(五)内联函数

如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。

空间换时间。

引入内联函数的目的是为了解决程序中函数调用的效率问题。

注意:内联函数内部不能有太复杂的逻辑,编译器有时会有自己的优化策略,所以内联函数不一定起作用。

四、C++高级语法

(一)面向对象:类

C++使用struct、class来定义一个类:

  • struct的默认成员权限是public;
  • class的默认成员权限是private
    除此之外,而这基本无差别。

(二)运算符重载

两个class 的 + 、- ,默认是不允许的,但我们可以通过运算符重载实现class的加减。

Complex operator+ (const Complex& x);


Complex Complex::operator+ (const Complex& x) {
    Complex tmp; // 在栈中创建一个临时变量
    tmp._real = _real + x._real;
    tmp._image = _image + x._image;
    return tmp;
}

上面代码是对的!

1. Comple tml 在栈上创建的变量是不是外面用不了?

你的代码片段是一个重载了加法运算符的成员函数。在这个函数中,你在栈上创建了一个临时变量tmp来存储计算结果。然后,将该临时变量作为返回值返回。

这段代码是没有问题的。在函数中创建的临时变量tmp是在栈上分配的,它的生命周期与函数的执行周期相同。当函数执行完毕后,临时变量tmp会被自动释放。

当你使用这个重载的加法运算符时,它会返回一个临时创建的Complex对象,你可以将其赋值给另一个Complex对象,或者使用它进行其他操作。

例如:

1
2
3
Complex a(1, 2);
Complex b(3, 4);
Complex c = a + b; // 使用重载的加法运算符,创建临时对象并赋值给c

在上述示例中,c是通过重载的加法运算符返回的临时对象。你可以在后续的代码中继续使用c对象,它的生命周期与其他对象相同。

需要注意的是,虽然临时对象的生命周期是短暂的,但在其存在期间,你可以正常地使用它。只要你在临时对象的生命周期内进行操作,它是可用的。但是在临时对象的生命周期结束后,尝试访问该对象将导致未定义的行为。

总结起来,你的代码没有问题。临时对象在栈上创建,它在其所在的作用域结束后会被自动释放。你可以在临时对象的生命周期内使用它,但在其生命周期结束后,应避免访问该对象。

2. Complex c 在它的作用域外就会被自动回收是吧?

是的,你理解得很正确。在你的代码中,当你在函数内部创建一个临时变量Complex tmp时,它的作用域仅限于该函数。当函数执行完毕后,临时变量tmp会被自动释放,也就是说,它的生命周期结束了。

在你的示例中,通过重载的加法运算符,临时对象tmp被创建并用于存储计算结果。然后,你将临时对象作为返回值返回,并将其赋值给Complex c。在这个过程中,c接收了临时对象的值,并成为了一个新的Complex对象。

当函数执行完毕时,临时对象tmp的作用域结束,它会被自动释放。然而,c作为一个独立的对象,它的生命周期并不受临时对象tmp的影响。c会继续存在,你可以在后续的代码中继续使用它。

3. Complex tmp和Complex c的地址一样吗?

在你的代码中,Complex tmpComplex c是两个不同的对象,它们在内存中分别有自己的地址。因此,tmpc的地址是不一样的!!!!!!

当你在函数内部创建一个临时变量Complex tmp时,它在栈上分配了一块内存,并拥有自己的地址。然后,通过赋值操作将临时对象的值传递给了另一个对象Complex c

虽然tmpc的值可能相同,但它们是两个独立的对象,拥有不同的内存地址。

你可以通过打印它们的地址来验证:

1
2
3
4
5
Complex tmp;
Complex c = tmp;

std::cout << "Address of tmp: " << &tmp << std::endl;
std::cout << "Address of c: " << &c << std::endl;

输出结果会显示tmpc的地址是不同的。

总结起来,Complex tmpComplex c是两个不同的对象,它们在内存中有各自的地址。尽管它们的值可能相同,但它们是独立的对象。

(三)拷贝构造及临时对象的优化

在栈上的对象被销毁时,也会触发析构函数。

return 时如果返回了栈上的对象,那么就会触发:拷贝构造 ,拷贝完成后该栈上的对象就会被析构。

覆写拷贝对象函数:

Complex(const Complex& x); // 拷贝构造

Complex::Complex(const Complex& x) {
    _real = x._real;
    _image = x._image;

    // 如果Complex包含堆上的对象,还会涉及深拷贝和浅拷贝,所以一般    都要重写类的拷贝构造函数。
}

章节延伸题思考

(一)C++中什么时候使用struct,什么时候使用union,什么时候使用class?

在C++中,struct、union和class都是用于定义自定义数据类型的关键字,它们之间的区别如下:

  1. struct:struct用于定义一个包含多个成员变量的数据结构。它的成员变量默认是公有的(public),并且可以包含成员函数。通常情况下,struct用于表示一组相关的数据,可以有不同的访问权限。

  2. union:union用于定义一种特殊的数据结构,它的所有成员共享同一块内存空间。union的成员变量可以有不同的数据类型,但是同一时间只能存储其中的一个成员。union通常用于节省内存,当你需要在不同的数据类型之间进行转换或共享内存时会很有用。

  3. class:class用于定义一个包含数据成员和成员函数的对象。它的成员变量和成员函数默认是私有的(private),需要通过公有的接口(公有成员函数)来访问。class通常用于封装数据和行为,实现面向对象的编程。

选择使用struct、union或class取决于你的需求和设计目标:

  • 如果你只需要简单地组织一些数据成员,可以使用struct。
  • 如果你需要在不同的数据类型之间进行转换或共享内存,可以使用union。
  • 如果你需要封装数据和行为,并且希望通过公有接口来访问和操作数据,可以使用class。

需要注意的是,struct、union和class之间的主要区别是默认的成员访问权限和默认的继承方式。但是在C++中,你可以使用访问修饰符(如public、private和protected)来显式地控制成员的访问权限,并且struct和class在语法上几乎是相同的,唯一的区别是默认的访问权限不同。

(二)结构体是struct和class有什么不同?

在C++中,struct和class在语法上几乎是相同的,唯一的区别是默认的成员访问权限和默认的继承方式。具体来说,struct和class之间的主要区别如下:

  1. 默认的成员访问权限:

    • 在struct中,默认的成员访问权限是公有的(public),即结构体的成员变量和成员函数默认是公开的,可以被外部访问。
    • 在class中,默认的成员访问权限是私有的(private),即类的成员变量和成员函数默认是私有的,只能在类的内部访问。
  2. 默认的继承方式:

    • 在struct中,默认的继承方式是公有继承(public inheritance),即结构体默认是以公有继承方式派生的。
    • 在class中,默认的继承方式是私有继承(private inheritance),即类默认是以私有继承方式派生的。

除了上述区别,struct和class在其他方面是相似的。它们都可以包含成员变量和成员函数,并且可以使用访问修饰符(如public、private和protected)来显式地控制成员的访问权限。

需要注意的是,这种区别只是默认行为,你可以在struct或class中使用访问修饰符来显式地指定成员的访问权限和继承方式,从而覆盖默认行为。因此,在实际使用中,struct和class可以互换使用,具体取决于你的需求和设计风格。

(三)class和struct的内存布局是连续的,还是不连续的?

在C++中,class和struct的内存布局是连续的。无论是class还是struct,它们都是用户定义的数据类型,其对象的内存布局是由成员变量和对齐规则共同决定的。

按照C++的规定,成员变量按照声明的顺序依次排列在对象的内存中。这意味着,成员变量在内存中是连续存储的,没有间隙或额外的填充字节。这样可以确保对象的内存布局是紧凑的,没有浪费空间。

需要注意的是,编译器可能会对对象的内存布局进行优化,例如进行字段重排或填充字节,以提高内存访问的效率。这些优化可能会导致对象的内存布局与代码中的声明顺序有所不同,但是成员变量仍然是连续存储的。

总结起来,class和struct的内存布局是连续的,成员变量按照声明的顺序依次排列在对象的内存中。这使得对象的内存布局是紧凑的,没有浪费空间。

(四)如果我有一个C++的类 Complex ,那么你建议我是使用 new Complex()来在堆中创建对象,还是使用Complex xx 在栈中创建对象?

在C++中,你可以选择在堆中使用new Complex()或在栈中使用Complex xx来创建对象,具体取决于你的需求和设计。

如果你需要在对象的生命周期超出当前作用域时仍然保持有效,或者需要在多个地方共享对象,那么使用堆内存创建对象可能更合适。使用new Complex()会在堆中分配内存,并返回指向堆中对象的指针。你可以使用这个指针来访问和操作对象,并且可以在适当的时候使用delete来释放堆内存。

1
2
3
Complex* ptr = new Complex();
// 使用ptr指针访问和操作对象
delete ptr; // 释放堆内存

另一方面,如果你只需要在当前作用域内使用对象,并且对象的生命周期在作用域结束时自动结束,那么使用栈内存创建对象可能更方便。使用Complex xx会在栈上分配内存,并在作用域结束时自动释放内存。

1
2
3
4
void someFunction() {
Complex xx;
// 使用xx对象
} // xx对象在作用域结束时自动释放内存

需要注意的是,使用堆内存创建对象需要手动管理内存,包括在适当的时候释放内存。而使用栈内存创建对象则由编译器自动管理内存,不需要手动释放。

综上所述,选择在堆中还是栈中创建对象取决于对象的生命周期和使用方式。如果对象需要在作用域外仍然有效或需要在多个地方共享,可以使用堆内存创建对象。如果对象只在当前作用域内使用,并且不需要手动管理内存,可以使用栈内存创建对象。

(五)如果我在C++中使用栈创建了一个对象,然后我把这个栈创建的对象使用一个指针进行引用,它会被自动释放吗?

在C++中,使用栈创建的对象的生命周期与其所在的作用域相关。如果你在栈中创建了一个对象,然后将其地址赋给指针进行引用,当对象所在的作用域结束时,对象会自动被释放,包括通过指针引用的对象。

考虑以下示例:

1
2
3
4
5
void someFunction() {
Complex xx; // 在栈中创建对象
Complex* ptr = &xx; // 使用指针引用对象
// 使用ptr指针访问和操作对象
} // xx对象所在的作用域结束,对象自动被释放

在这个示例中,Complex xxsomeFunction函数中被创建,它是一个栈上的对象。然后,通过Complex* ptr = &xx将对象的地址赋给指针ptr。在ptr指针所在的作用域结束时,即someFunction函数结束时,栈上的对象xx会自动被释放,包括通过指针ptr引用的对象。

需要注意的是,尽管指针ptr仍然保留着对象的地址,但是在对象所在的作用域结束后,访问指针ptr所指向的对象将会导致未定义的行为。因此,在对象所在的作用域结束后,应该避免使用指针引用该对象。

总结起来,使用栈创建的对象在其所在的作用域结束时会自动被释放,包括通过指针引用的对象。因此,你不需要手动释放栈上的对象。