【C++】 学习笔记


C++是一种静态类型的编译语言,使用静态类型的编程语言是在编译时执行类型检查,而不是运行时执行类型检查。

面向对象开发的四大特性

封装:将数据和方法组合在一起,对外部隐藏实现细节,只公开对外提供的接口,提高安全性、可靠性和灵活性。
继承:从已有类中派生出新类,新类具有已有类的属性和方法,并且可以扩展或修改这些属性和方法,提高代码的复用性和可拓展性。
多态:指同一种操作作用于不同的对象,可以有不同的解释和实现。它可以通过接口或继承实现,可以提高代码的灵活性和可读性。
抽象:从具体的实例中提取共同的特征,形成抽象类或接口,以便于代码的复用和拓展。抽象类和接口可以让程序员专注于高层次的设计和业务逻辑,而不必关注底层的实现细节。

标准库

标准的C++由三个重要组成部分:
核心语言,提供了所有构建块,包括变量、数据类型和常量等
C++标准库,提供了大量函数,用于操作文件、字符串等
标准模板库(STL),提供了大量的方法,用于操作数据结构等。

print打印输出

#inclue <iostream>
using namespace std;
int main()
{
	cout << "HELLO WORLD" << endl;
	return 0;
}

endl将在每一行后插入一个换行符,<<用于向屏幕传多个值。

数据类型

typedef声明

typedef int feet;

即feet是int的另一个名称。

枚举类型

“枚举”是将变量的值一一列举出来。

enum 枚举名{
	标识符[=整型常数]...
	标识符[=整型常数]
}枚举变量;

[=整型常数]部分可以省略。
例如,

enum color{red,green,blue}c;
c=blue;

默认情况下,red=0。

enum color{red,green=5,blue}c;

这里red=0,blue=6。

类型转换

将一个数据类型的值转换为另一种数据类型的值,C++有四种类型转换:静态、动态、常量和重新解释转换。
静态转换是将一种数据类型强制转换成另一种数据类型的值。常用于比较类型相似的对象之间的转换,如int->float。
不进行任何运行时类型检查,因此可能导致运行时错误。

int i=10;
float f = static_cast<float>(i);

动态转换常用于将一个基类指针或引用转换为派生类指针或引用。
在运行时进行类型检查,若不能进行转换则返回空指针或引发异常。

class Base {};
class Derived : public Base {};
Base* ptr_base = new Derived;
Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); // 将基类指针转换为派生类指针

常量转换常用于将const类型的对象转换为非const类型的对象。
只能用于转换掉const属性,不能改变对象的类型(没有真正去除)。
(1)语言的类型的强制转换不会修改原来的数据,会另外的开辟一个临时的或者程序中指定的空间来存储强制转换后的值。
(2)C++引用的实现是在符号表中动了手脚,把自己的变量符号对应的内存地址写成了它所引用的那个变量的内存地址了。
(3)C++的cout函数的执行,是根据变量的名称找到对应的内存地址,然后根据变量的类型从内存中抓取相应的数据。

const int i = 10;
int& r = const_cast<int&>(i);

重新解释转换将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换。
重新解释转换不进行任何类型检查,因此可能会导致未定义的行为。

int i = 10;
float f = reinterpret_cast<float&>(i); // 重新解释将int类型转换为float类型

(int)类型和(int &)类型的区别

int a定义一个变量a;
int &a定义一个引用,可以说是一个变量的别名,在定义时必须绑定到另一个变量。

int i = 0;
int& n = i;
n++;

此时,i=1。
int &一般用于函数参数中。

变量声明

当使用多个文件且只在其中一个文件中定义变量时,可以使用extern关键字在任何地方声明一个变量。

左值和右值

int g = 20;

变量g是左值,是指向内存的表达式;
数值20是右值,是存储在内存中的某些地址的数值,不能被赋值。

变量作用域

局部作用域

全局作用域

块作用域

代码块访问。

类作用域

#include <iostream>

class MyClass {
public:
    static int class_var;  // 类作用域变量
};

int MyClass::class_var = 30;

int main() {
    std::cout << "类变量: " << MyClass::class_var << std::endl;
    return 0;
}

MyClass声明了一个class_var类作用域变量。使用类名和作用域解析运算符::来访问该变量。

常量

整数常量

前缀0x十六进制;0八进制;
后缀U无符号整数(unsigned);L长整数(long)。

浮点常量

3.14159       // 合法的 
314159E-5L    // 合法的 
510E          // 非法的:不完整的指数
210f          // 非法的:没有小数或指数
.e55          // 非法的:缺少整数或分数

布尔常量、字符常量、字符串常量

如何定义常量?(#define;const)

一般定义为大写。

1.#define预处理器

#include <iostream>
using namespace std;
#define LENGTH 10   
#define WIDTH  5
#define NEWLINE '\n'
int main()
{
   int area;  
   area = LENGTH * WIDTH;
   cout << area;
   cout << NEWLINE;
   return 0;
}

2.const关键字

#include <iostream>
using namespace std;
int main()
{
   const int  LENGTH = 10;
   const int  WIDTH  = 5;
   const char NEWLINE = '\n';
   int area;  
 
   area = LENGTH * WIDTH;
   cout << area;
   cout << NEWLINE;
   return 0;
}

存储类

定义C++程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。
可用的存储类:
auto;register;static;extern;mutable;thread_local

auto

根据初始化表达式自动推断被声明的变量的类型。

auto f=3.14;      //double
auto s("hello");  //const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型

register

用于定义存储在寄存器(可能)中而不是RAM中的局部变量。一般用于需要快速访问的变量,比如计数器。
不能使用&运算符,因为它没有内存位置。

寄存器容量小,速度快,常用于存储CPU内部的临时数据和控制指令。
RAM用于存储需要频繁读写的数据,例如程序运行中的变量和缓存等。

static

在程序的生命周期内保持局部变量的存在。

static和extern区别

static只能在该编译单元使用;extern可以不在当前编译单元编译。
参考https://blog.csdn.net/weixin_43811423/article/details/128347913

1.在类外使用static

1.1 例子:extern与static变量声明
Static.cpp:

int s_Variable = 5;

Main.cpp:

extern int s_Variable;
int main(){
    cout << s_Variable << endl;
}

由于Main.cpp中指明extern,此时编译器在连接时,会去当前编译单元之外的地方寻找变量s_Variable。

如果将Static.cpp改成如下:
Static.cpp:

static int s_Variable = 5;

此时连接时就会报错找不到s_Variable,因为s_Variable的作用域只在Static.cpp中。

1.2 例子:static函数声明
Static.cpp:

int func()
{
	return 9;
}

Main.cpp:

int func()
{
	return 8;
}
int main(){
    cout << func() << endl;
}

此时就会出现连接错误:函数重复定义。将Main.cpp的func修改如下:(修改Static.cpp的也行)

static int func()
{
	return 8;
}

程序正常运行,并且输出8。(如上情况中,连接时会优先使用本编译单元的静态函数,忽略其他编译单元的重复函数定义。Main.cpp中的static int func()会优先作用,忽略其他编译单元的int func())

如果函数或变量不会在其他编译单元使用,那么就把它们定义为静态的。

2.类内static

2.1 静态变量
在一个类内声明变量是static,此后这个变量将成为该类所有实例化对象的共享数据。
这里用struct举例(class同理)

struct Entity
{
    int x, y;
    void Print()
    {
        cout << x << ", " << y << endl;
    }
};

int main(){
    Entity e;
    e.x = 2;
    e.y = 3;
    Entity e1 = {5, 8};
    e.Print();
    e1.Print();
    // 输出2,3和5,8
}

将Entity中的x,y 设置成静态成员变量后(此时不能再用Entity e1 = {5, 8}这种初始化方式):

struct Entity
{
    static int x, y;
    void Print()
    {
        cout << x << ", " << y << endl;
    }
};

// 声明成静态后要加作用域的声明
int Entity::x;
int Entity::y;

int main(){
    Entity e;
    e.x = 2;
    e.y = 3;
    Entity e1;
    e1.x = 5;
    e1.y = 8;
    e.Print();
    e1.Print();
    // 输出都是5,8
}

一个小重点
没有声明那两行代码时,编译会报错,提示增加Entity::这两个声明。从这里也可以看出,x和y不再属于任何一个实例对象,而是Entity整个作用域的变量。 即Entity类的不同对象的静态成员xy分别指向同一地址。因此任意一个实例对象对xy的修改都会导致xy的变化。上述e.x或e1.x的调用方式实际上等价于Entity::x,也就是说xy并不属于某个具体对象,而是这个作用域。创建新类分配存储地址时也与xy无关。

2.2 静态函数
静态函数只能访问静态变量,不能访问非静态变量。
正常访问举例:

struct Entity
{
	// 变量和函数都是静态的
    static int x, y;    
    static void Print()
    {
        cout << x << ", " << y << endl;
    }
};
// 需要声明
int Entity::x;
int Entity::y;

int main(){
	// 通过域名使用
    Entity::x = 1;
    Entity::y = 2;
    Entity::Print();
    
}

当xy不再是静态时,静态方法访问他们会报错(Print()中)。这是因为静态方法没有类实例,所以不知道非静态变量指的是什么。非静态方法会获取当前类的一个实例作为参数(this指针),因此知道xy是当前类的变量xy。上述静态方法和非静态方法等价于如下类外函数:

// 静态方法
static void Print()
{
    cout << x << ", " << y << endl;
    // 报错:Use of undeclared identifier 'x'
}

// 非静态方法
static void Print(Entity e)
{
    cout << e.x << ", " << e.y << endl;
}

3. Local static
局部变量的生存周期在执行完这个局部作用域的命令后结束(例如函数),但在这个局部作用域内将该变量设为static可以使他的生存周期到程序运行结束(生存周期改变)。但作用域还是这个作用域,这意味着作用域以外的地方想访问这个变量依旧是不行的(保留局部变量可见性的特点)。

3.1 例子:普通变量

void func()
{
    static int i = 0;
    i++;
    cout << i << endl;
}

int main(){
    func();
    func();
    func();
    func();
    // 输出为1,2,3,4
}

也就是说,单次func函数执行结束后i并没有消失。如果不是static,输出的结果将都是1。这里static相当于在func外定义了只有func函数可见的公共i,这保证了数据的安全性。当然不用static,用类设置i为私有静态成员也是一样的效果,只不过太麻烦,局部静态可以让代码更简洁。
静态和非静态主要看变量的生命周期,非静态执行完一次就没了,但是静态的生命可以持续到整个程序结束。

运算符

<<二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)
>>二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

循环

循环类型

while

while循环可能一次都不会执行。

for

for(;;)常常被用来表示无限循环。按CTRL+C可以终止一个无线循环。

int my_array[5] = {1,2,3,4,5};
for (int &x:my_array)
{

	x*=2;
	cout<<x<<endl;
}
//这里int也可以改成auto,自动获取变量的类型
#inclue<iostream>
#inclue<string>
#inclue<cctype>
using namespace std;
int main()
{
	string str("some string");
	for(auto &c:str)
	{
		c = toupper(c);
	}
	cout << str << endl;
	return 0;
}
//输出:SOME STRING

do…while

至少会执行一次循环。

嵌套循环

当执行离开一个范围时,所有在该范围中创建的自动对象都会被销毁。

break

立刻终止最内层的循环。

continue

跳过当前循环中的代码,强迫开始下一次循环。

goto

将控制转移到同一函数内的被标记的语句。
可以退出深嵌套例程:

for(...) {
   for(...) {
      while(...) {
         if(...) goto stop;
         .
         .
         .
      }
   }
}
stop:
cout << "Error in program.\n";

函数

// 函数返回两个数中较大的那个数
 
int max(int num1, int num2) 
{
   // 局部变量声明
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}

函数声明:

int max(int num1, int num2);

函数声明中,参数的名称并不重要,只有参数类型必须,所以下面也是对的:

int max(int, int);

调用函数:

#include <iostream>
using namespace std;
 
// 函数声明
int max(int num1, int num2);
 
int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;
   int ret;
   // 调用函数来获取最大值
   ret = max(a, b);
   cout << "Max value is : " << ret << endl;
   return 0;
}
 
// 函数返回两个数中较大的那个数
int max(int num1, int num2) 
{
   // 局部变量声明
   int result;
   if (num1 > num2)
      result = num1;
   else
      result = num2; 
   return result; 
}
//输出:200

函数参数:
传值调用-将参数的实际值赋值给函数的形参。修改函数内的形式参数对实际参数没有影响。
指针调用-将参数的地址赋值给形参。修改形参会影响实参。

// 函数定义
void swap(int *x, int *y)
{
   int temp;
   temp = *x;    /* 保存地址 x 的值 */
   *x = *y;        /* 把 y 赋值给 x */
   *y = temp;    /* 把 x 赋值给 y */  
   return;
}

#include <iostream>
using namespace std;
// 函数声明
void swap(int *x, int *y);
int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200; 
   cout << "交换前,a 的值:" << a << endl;
   cout << "交换前,b 的值:" << b << endl;
   /* 调用函数来交换值
    * &a 表示指向 a 的指针,即变量 a 的地址 
    * &b 表示指向 b 的指针,即变量 b 的地址 
    */
   swap(&a, &b);
   cout << "交换后,a 的值:" << a << endl;
   cout << "交换后,b 的值:" << b << endl;
   return 0;
}

引用调用-将参数的引用赋值给形参。修改形参会影响实参。
引用传参在处理大型数据时,节省时间。

#include <iostream>
using namespace std; 
// 函数声明
void swap(int &x, int &y); 
int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200; 
   cout << "交换前,a 的值:" << a << endl;
   cout << "交换前,b 的值:" << b << endl; 
   /* 调用函数来交换值 */
   swap(a, b); 
   cout << "交换后,a 的值:" << a << endl;
   cout << "交换后,b 的值:" << b << endl; 
   return 0;
}
 
// 函数定义
void swap(int &x, int &y)
{
   int temp;
   temp = x; /* 保存地址 x 的值 */
   x = y;    /* 把 y 赋值给 x */
   y = temp; /* 把 x 赋值给 y  */  
   return;
}

有关引用参考:
https://blog.csdn.net/Brant_zero/article/details/125837158
引用是给已存在的变量取一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用一块内存空间。

类型 & 引用变量名(对象名) = 引用实体
#include <iostream>
using namespace std; 
int main ()
{
   // 局部变量声明
   int a = 10;
    int &ra = a;
    cout << ra << endl;
}

输出:10,说明ra就是a的另一个名字。地址&ra和&a相同。
引用特性:
1.引用在定义时必须初始化;
2.一个变量可以有多个引用;
3.引用一旦引用一个实体,再不能引用其他实体(不能更改)。
用引用做形参,就避免了使用指针。
函数的返回值如果为函数内部定义变量的引用,函数在调用完毕后,函数内部定义的变量空间被释放,会无法访问报错。
解决办法:加static或者去掉引用返回,参考:https://blog.csdn.net/qq_40016998/article/details/104837400
所以,出了函数作用域,返回对象就销毁了,那么一定不能用传引用返回,一定要使用传值返回!
避免使用局部变量做引用返回!!!

引用和指针区别

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求。
  3. 引用在初始化引用一个实体后,就不能再引用其他实体,而指针可以在任何一个同类型实体。
  4. 没有NULL引用,但有NULL指针了。
  5. 在 sizeof 中含有不同:引用结果为未引用类型的大小,但指针始终是地址空间所占字节数(32为平台下占4个字节,64位平台下占8个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显示解引用,引用是编译器自己去处理。
  9. 引用比指针使用起来更加安全。

Lambda函数与表达式

[capture](parameters)->return-type{body}
[](int x, int y){ return x < y ; }
[](int x, int y) -> int { int z = x + y; return z + x; }
[]      // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&]     // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=]     // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x]  // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。

另外有一点需要注意。对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入:

[this]() { this->someFunc(); }();

数组

多维数组:

#include <iostream>
using namespace std;
 
int main ()
{
   // 一个带有 5 行 2 列的数组
   int a[5][2] = { {0,0}, {1,2}, {2,4}, {3,6},{4,8}};
 
   // 输出数组中每个元素的值                      
   for ( int i = 0; i < 5; i++ )
      for ( int j = 0; j < 2; j++ )
      {
         cout << "a[" << i << "][" << j << "]: ";
         cout << a[i][j]<< endl;
      }
 
   return 0;
}

setw()函数

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    // 开头设置宽度为 4,后面的 runoob 字符长度大于 4,所以不起作用
    cout << setw(4) << "runoob" << endl;
    // 中间位置设置宽度为 4,后面的 runoob 字符长度大于 4,所以不起作用
    cout << "runoob" << setw(4) << "runoob" << endl;
    // 开头设置间距为 14,后面 runoob 字符数为6,前面补充 8 个空格 
    cout << setw(14) << "runoob" << endl;
    // 中间位置设置间距为 14 ,后面 runoob 字符数为6,前面补充 8 个空格 
    cout << "runoob" << setw(14) << "runoob" << endl;
    cout << setfill('*')  << setw(14) << "runoob" << endl;
    return 0;
}
}

输出:

runoob
runoobrunoob
        runoob
runoob        runoob
********runoob

指针

#include <iostream>
 
using namespace std;
 
int main ()
{
   int  var = 20;   // 实际变量的声明
   int  *ip;        // 指针变量的声明
 
   ip = &var;       // 在指针变量中存储 var 的地址
 
   cout << "Value of var variable: ";
   cout << var << endl;
 
   // 输出在指针变量中存储的地址
   cout << "Address stored in ip variable: ";
   cout << ip << endl;
 
   // 访问指针中地址的值
   cout << "Value of *ip variable: ";
   cout << *ip << endl;
 
   return 0;
}

空指针NULL指针

变量声明时,如果没有确切的地址可以赋值,可以为指针变量赋一个NULL值。赋为NULL的指针称为空指针。
NULL指针值为0.
空指针和未初始化指针是不同的,空指针的值是确定的0,未初始化指针包含的值是随机的。所以,在使用指针之前最好先将其初始化NULL或nullptr避免在程序中出现未定义行为。
(nullptr能够自动推导出所需的指针类型,避免在定义指针时与其他类型混淆,使用nullptr的优点在于它可以更加安全地代替原来C++中使用的NULL宏定义,避免了出现空指针和整型0之间的混淆。)
在使用指针前一般先判断该指针是否为空:

if(ptr)     /* 如果 ptr 非空,则完成 ,等同于if (p != nullptr)*/
if(!ptr)    /* 如果 ptr 为空,则完成 */

指针的算术运算

根据指针的类型和大小来决定移动的距离。
移动n个元素的大小。
指针的比较:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   // 指针中第一个元素的地址
   ptr = var;
   int i = 0;
   while ( ptr <= &var[MAX - 1] )
   {
      cout << "Address of var[" << i << "] = ";
      cout << ptr << endl;
 
      cout << "Value of var[" << i << "] = ";
      cout << *ptr << endl;
 
      // 指向上一个位置
      ptr++;
      i++;
   }
   return 0;
}

指向数组的指针

一个指向数组开头的指针,可以通过指针的算术运算或数组索引来访问数组。

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   // 指针中的数组地址
   ptr = var;
   for (int i = 0; i < MAX; i++)
   {
      cout << "var[" << i << "]的内存地址为 ";
      cout << ptr << endl;
 
      cout << "var[" << i << "] 的值为 ";
      cout << *ptr << endl;
 
      // 移动到下一个位置
      ptr++;
   }
   return 0;
}
/*输出:
var[0]的内存地址为 0x7fff59707adc
var[0] 的值为 10
var[1]的内存地址为 0x7fff59707ae0
var[1] 的值为 100
var[2]的内存地址为 0x7fff59707ae4
var[2] 的值为 200
*/

数组中用的是*ptr = var而不是*ptr =&var ,其中,var是数组,ptr是指针,此时指针指向的是数组中的第一个元素,用*ptr可以读取其中的元素。ptr++表示指针向下移动一位。

存储指针的数组

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int *ptr[MAX];
 
   for (int i = 0; i < MAX; i++)
   {
      ptr[i] = &var[i]; // 赋值为整数的地址
   }
   for (int i = 0; i < MAX; i++)
   {
      cout << "Value of var[" << i << "] = ";
      cout << *ptr[i] << endl;
   }
   return 0;
}
/*
输出:
Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200
*/

从函数返回指针

C++不支持在函数外返回局部变量的地址,除非定义局部变量为static变量。
例子:生成10个随机数,使用表示指针的数组名(即第一个数组元素的地址)来返回它们,具体如下:

#include <iostream>
#include <ctime>
#include <cstdlib> 
using namespace std;
 
// 要生成和返回随机数的函数
int * getRandom( )
{
  static int  r[10];
  // 设置种子
  srand( (unsigned)time( NULL ) );
  for (int i = 0; i < 10; ++i)
  {
    r[i] = rand();
    cout << r[i] << endl;
  }
  return r;
}
 
// 要调用上面定义函数的主函数
int main ()
{
   // 一个指向整数的指针
   int *p; 
   p = getRandom();
   for ( int i = 0; i < 10; i++ )
   {
       cout << "*(p + " << i << ") : ";
       cout << *(p + i) << endl;
   } 
   return 0;
}
/*输出:
624723190
1468735695
807113585
976495677
613357504
1377296355
1530315259
1778906708
1820354158
667126415
*(p + 0) : 624723190
*(p + 1) : 1468735695
*(p + 2) : 807113585
*(p + 3) : 976495677
*(p + 4) : 613357504
*(p + 5) : 1377296355
*(p + 6) : 1530315259
*(p + 7) : 1778906708
*(p + 8) : 1820354158
*(p + 9) : 667126415
*/

for循环中的输出也可以改成:

cout << *p << endl;
p++;

数据结构

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book;

结构体名字Books,变量为book,可以为多个。
使用.来访问结构的内部:

#include <iostream>
#include <cstring>
 
using namespace std;
void printBook( struct Books book );
 
// 声明一个结构体类型 Books 
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
 
int main( )
{
   Books Book1;        // 定义结构体类型 Books 的变量 Book1
   Books Book2;        // 定义结构体类型 Books 的变量 Book2
 
    // Book1 详述
   strcpy( Book1.title, "C++ 教程");
   strcpy( Book1.author, "Runoob"); 
   strcpy( Book1.subject, "编程语言");
   Book1.book_id = 12345;
 
   // Book2 详述
   strcpy( Book2.title, "CSS 教程");
   strcpy( Book2.author, "Runoob");
   strcpy( Book2.subject, "前端技术");
   Book2.book_id = 12346;
 
   // 输出 Book1 信息
   printBook( Book1 );
 
   // 输出 Book2 信息
   printBook( Book2 );
 
   return 0;
}
void printBook( struct Books book )
{
   cout << "书标题 : " << book.title <<endl;
   cout << "书作者 : " << book.author <<endl;
   cout << "书类目 : " << book.subject <<endl;
   cout << "书 ID : " << book.book_id <<endl;
}

使用指针指向结构体时,使用->访问结构的成员:

#include <iostream>
#include <cstring>
 
using namespace std;
void printBook( struct Books *book );
 
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
 
int main( )
{
   Books Book1;        // 定义结构体类型 Books 的变量 Book1
   Books Book2;        // 定义结构体类型 Books 的变量 Book2
 
    // Book1 详述
   strcpy( Book1.title, "C++ 教程");
   strcpy( Book1.author, "Runoob"); 
   strcpy( Book1.subject, "编程语言");
   Book1.book_id = 12345;
 
   // Book2 详述
   strcpy( Book2.title, "CSS 教程");
   strcpy( Book2.author, "Runoob");
   strcpy( Book2.subject, "前端技术");
   Book2.book_id = 12346;
 
   // 通过传 Book1 的地址来输出 Book1 信息
   printBook( &Book1 );
 
   // 通过传 Book2 的地址来输出 Book2 信息
   printBook( &Book2 );
 
   return 0;
}
// 该函数以结构指针作为参数
void printBook( struct Books *book )
{
   cout << "书标题  : " << book->title <<endl;
   cout << "书作者 : " << book->author <<endl;
   cout << "书类目 : " << book->subject <<endl;
   cout << "书 ID : " << book->book_id <<endl;
}

typedef关键字

为创建的类型取一个别名:

typedef struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
}Books;
Books Book1,Book2;

typedef long int *pint32;
pint32 x, y, z;//x, y 和 z 都是指向长整型 long int 的指针。

类&对象

类class包括成员变量和成员函数。

class Box
{
   public://也可以为private,protected
      double length;   // 盒子的长度
      double breadth;  // 盒子的宽度
      double height;   // 盒子的高度
};

例子:

#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
      // 成员函数声明
      double get(void);
      void set( double len, double bre, double hei );
};
// 成员函数定义
double Box::get(void)
{
    return length * breadth * height;
}
 
void Box::set( double len, double bre, double hei)
{
    length = len;
    breadth = bre;
    height = hei;
}
int main( )
{
   Box Box1;        // 声明 Box1,类型为 Box
   Box Box2;        // 声明 Box2,类型为 Box
   Box Box3;        // 声明 Box3,类型为 Box
   double volume = 0.0;     // 用于存储体积
 
   // box 1 详述
   Box1.height = 5.0; 
   Box1.length = 6.0; 
   Box1.breadth = 7.0;
 
   // box 2 详述
   Box2.height = 10.0;
   Box2.length = 12.0;
   Box2.breadth = 13.0;
 
   // box 1 的体积
   volume = Box1.height * Box1.length * Box1.breadth;
   cout << "Box1 的体积:" << volume <<endl;
 
   // box 2 的体积
   volume = Box2.height * Box2.length * Box2.breadth;
   cout << "Box2 的体积:" << volume <<endl;
 
 
   // box 3 详述
   Box3.set(16.0, 8.0, 12.0); 
   volume = Box3.get(); 
   cout << "Box3 的体积:" << volume <<endl;
   return 0;
}
/*输出:
Box1 的体积:210
Box2 的体积:1560
Box3 的体积:1536
*/

类成员函数的定义

可以在类内直接定义,也可以在类外使用类名::函数进行定义,::为范围解析运算符。

类访问修饰符public,private,protected

数据封装可以防止函数直接访问类类型的内部成员。
成员和类默认访问修饰符是private
程序中类外部是可访问的,可以不使用任何成员函数来设置或获取public变量的值。

#include <iostream>
using namespace std;
class Line{
	double width;
    public:
        double length;
        void setLength(double len);
        double getLength(void);
};
double Line::getLength(void){
    return length;
}
void Line::setLength(double len){
    length = len;
}

int main(){
    Line line;
    line.setLength(6.0);
    cout << "Length of line:" << line.getLength() << endl;
    line.length = 10.0;
    cout << line.length << endl;
    
    line.width = 3.0//错误
    line.setwidth(3.0);
    cout << line.getwidth() <<endl;
    return 0;
}

一般情况下,数据定义为私有,函数定义为公有,以便在类外部可以调用。
如果是private变量,不能直接访问,需要通过public函数来访问。
protected和private相似,但在子类可以访问
有public, protected, private三种继承方式,它们相应地改变了基类成员的访问属性。
继承中的public,protected,private:
1.public 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private
2.protected 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private
3.private 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private

但无论哪种继承方式,上面两点都没有改变:
1.private 成员只能被本类成员(类内)和友元访问,不能被派生类访问;
2.protected 成员可以被派生类访问。

#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:
  int a;
  A(){
    a1 = 1;
    a2 = 2;
    a3 = 3;
    a = 4;
  }
  void fun(){
    cout << a << endl;    //正确
    cout << a1 << endl;   //正确
    cout << a2 << endl;   //正确
    cout << a3 << endl;   //正确
  }
public:
  int a1;
protected:
  int a2;
private:
  int a3;
};
class B : protected A{
public:
  int a;
  B(int i){
    A();
    a = i;
  }
  void fun(){
    cout << a << endl;       //正确,public成员。
    cout << a1 << endl;       //正确,基类的public成员,在派生类中变成了protected,可以被派生类访问。
    cout << a2 << endl;       //正确,基类的protected成员,在派生类中还是protected,可以被派生类访问。
    cout << a3 << endl;       //错误,基类的private成员不能被派生类访问。
  }
};
int main(){
  B b(10);
  cout << b.a << endl;       //正确。public成员
  cout << b.a1 << endl;      //错误,protected成员不能在类外访问。
  cout << b.a2 << endl;      //错误,protected成员不能在类外访问。
  cout << b.a3 << endl;      //错误,private成员不能在类外访问。
  system("pause");
  return 0;
}

类构造函数&析构函数

类的构造函数会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,不会返回任何类型,也不会返回void。可用于为某些成员变量设置初始值。

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();  // 这是构造函数
 
   private:
      double length;
};
 
// 成员函数定义,包括构造函数
Line::Line(void)
{
    cout << "Object is being created" << endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
   Line line;
 
   // 设置长度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}
/*
输出:
Object is being created
Length of line : 6
*/

默认的构造函数没有任何参数,即类名后面加个括号;构造函数也可以带有参数,在创建对象时就会给对象赋初始值:
将上面代码的Line()函数改为:

Line::Line(double len){
	length = len;
}

int main中加入:

Line line(5.0);

就可以在创建line对象时给line的length赋值为5.0了。
使用初始化列表来初始化字段:

Line::Line( double len): length(len)
{
    cout << "Object is being created, length = " << len << endl;
}

和上面的赋值是一个意思,如果有多个要赋值的字段:

C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
  ....
}

析构函数:
与类名相同,在前面加了一个~符号,它会在每次删除所创建的对象时执行,有助于在跳出程序(如关闭文件、释放内存等)前释放资源。

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();   // 这是构造函数声明
      ~Line();  // 这是析构函数声明
 
   private:
      double length;
};
 
// 成员函数定义,包括构造函数
Line::Line(void)
{
    cout << "Object is being created" << endl;
}
Line::~Line(void)
{
    cout << "Object is being deleted" << endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
   Line line;
 
   // 设置长度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}

输出:

Object is being created
Length of line : 6
Object is being deleted

拷贝构造函数

使用另一个同类型的对象来初始化新创建的对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值