Linux嵌入式学习——C++学习(2)

一、标识符的作用域和可见性

(一)作用域

1、全局作用域

在函数外部声明的变量和函数具有全局作用域。这些变量和函数在程序的任何地方都可以被访问。

2.局部作用域

在函数内部、循环体内部或条件语句内部声明的变量具有局部作用域。这些变量只能在其声明的代码块内部被访问。

#include <iostream>  
void localScopeFunc() {  int localVar = 20; // 局部变量  std::cout << localVar << std::endl; // 输出: 20  
}  int main() {  localScopeFunc();  // std::cout << localVar << std::endl; // 错误!localVar 在此处不可见  return 0;  
}

3、类作用域

在类中声明的成员变量和成员函数具有类作用域。它们只能通过类的对象或指针来访问。

#include <iostream>  
class MyClass {  
public:  int x;  void printX() {  std::cout << "x = " << x << std::endl;  }  
};  int main() {  MyClass obj;  obj.x = 10;  obj.printX(); // 输出: x = 10  return 0;  
}

4、命名空间作用域(Namespace Scope)

        一个大型的程序通常由不同模块构成,不同的模块甚至有可能是由不同人员开发的。不同模块中的类和函数之间有可能发生重名,这样就会引发错误。这就好像上海和武汉都有南京路,如果在缺乏上下文的情况下直接说出“南京路”,就会产生歧义。但如果说“上海的南京路”或“武汉的南京路”,歧义就会消除。命名空间起到的就是这样的作用。

#include <iostream>  namespace CD {  
class clock {  
public:  int x;  clock() : x(30) {} // 构造函数中初始化x  void namespaceFunc() {  std::cout << "Function in CD called." << std::endl;  }  
};  
} // namespace CD  namespace xian {  
class clock {  
public:  int x;  clock() : x(30) {} // 构造函数中初始化x  void namespaceFunc() {  std::cout << "Function in xian called." << std::endl;  }  
};  
} // namespace xian  int main() {  CD::clock cdClock;  std::cout << cdClock.x << std::endl; // 输出: 30  cdClock.namespaceFunc(); // 调用CD命名空间中的函数  xian::clock xianClock;  std::cout << xianClock.x << std::endl; // 输出: 30  xianClock.namespaceFunc(); // 调用xian命名空间中的函数  return 0;  
}

也可以在main函数中

在上述示例中都是std::XXX的情况是由于没有在最开头提出命名空间域 using namespace std

(二)可见性

作用域之间是可以相互包含的关系

  1. 标识符要声明在前,引用在后
    • 在使用任何标识符之前,必须先声明它。这意味着你必须在代码中先定义变量、函数或其他标识符,然后才能使用它们。
  2. 在同一作用域中,不能声明同名的标识符
    • 在同一个作用域内,你不能声明两个具有相同名称的标识符。例如,在同一个函数内,你不能声明两个同名的局部变量。
  3. 在没有互相包含关系的不同的作用域中声明的同名标识符,互不影响
    • 如果两个标识符在不同的作用域中声明,并且这些作用域之间没有包含关系,那么这两个标识符是独立的,互不影响。例如,一个函数内的局部变量和一个全局变量可以有相同的名称,它们不会互相干扰。
  4. 如果在两个或多个具有包含关系的作用域中声明了同名标识符,则外层标识符在内层不可见
    • 如果一个标识符在一个作用域内被声明,并且在另一个包含它的作用域内也被声明(同名),那么内层作用域中的标识符会隐藏外层作用域中的同名标识符。这意味着在内层作用域中,你只能访问内层声明的标识符,而不能访问外层声明的同名标识符。

二、生存期

1、静态生存期

定义:静态生存期指的是对象的生存期和程序的运行期相同,即只要程序开始运行(主程序运行之),该对象就被分配了内存,并且在程序结束之前始终存在。

特点

  1. 持久性:具有静态生存期的对象在程序的整个运行期间都存在,不会被自动销毁
  2. 作用域:虽然静态生存期与程序的运行期相同,但静态对象的作用域可能受到声明位置的影响。例如,在全局作用域中声明的静态
  3. 对象具有全局可见性,而在函数内部声明的静态局部变量则仅在函数内部可见。
  4. 初始化:静态对象通常只初始化一次,无论它们被访问多少次。对于静态局部变量,初始化发生在函数第一次调用时,之后即使函数多次被调用,静态局部变量也不会被重新初始化。

实现方式

  • 在全局作用域中声明的对象默认为静态生存期。
  • 函数内部局部作用域中,可以使用static关键字显式声明静态对象。

2、动态生存期

定义:动态生存期指的是对象的生命周期是动态的,可以在程序运行时根据需要创建和销毁。

特点

  1. 灵活性:动态生存期的对象可以根据程序的需要在运行时创建和销毁,提供了更高的灵活性。
  2. 作用域:动态生存期的对象的作用域通常限于创建它们的代码块或函数中。
  3. 手动管理:与静态生存期对象不同,动态生存期对象需要程序员手动管理其生命周期,包括创建和销毁。这通常涉及内存分配和释放操作,如使用newdelete操作符(在C++中)或mallocfree函数(在C中)。

3、举例

(1)在全局上声明Text 类的全局变量mit;

(2)用static修饰类Test的变量t,使其在整个程序结束时才销毁。

就算调用两次 foo()也只创建销毁一次因为是静态生存期内的。

如果t时动态生存期内的话

(3)未被初始化的静态生存期会被置零。

就算是成员变量未被初始化,在定义一个静态生存期的变量后,也是会被置零。

(4)匿名对象是动态生存期

当对象被使用完之后会立马被销毁

可以给他个别名(匿名对象的引用需要给他一个const),导致其生存期变长了,

但是这样无法调用showtime()函数【这个函数在clock类中】这个涉及到右值引用的问题

左值和右值

lvalue-locatable

rvalue -readable

在C++中,左值(lvalue)和右值(rvalue)是表达式属性的一部分,它们决定了表达式如何使用和它们可以出现在哪些上下文中。

  • 左值:可以出现在赋值语句的左侧,表示内存中的一个位置。左值可以是变量、数组元素、结构体成员、函数返回值(如果该函数返回引用或指针)等。左值具有持久的状态,可以被多次读取和修改。

  • 右值:不能出现在赋值语句的左侧,表示一个临时的值。右值可以是字面量、表达式的结果等。右值通常是匿名的,没有持久的状态,读取后就会被销毁。

在C++11及更高版本中,引入了右值引用(rvalue reference),用&&表示,它允许我们引用右值,从而可以延长右值的生命周期或对其进行操作,而不会立即销毁它们。

判断一个是左值还是右值,对其取地址,如果可以取地址就是可以定位的,即左值,

无法取地址就是无法取对,即右值

在C++中,所有的匿名变量都是右值。

左值 

           int i;

       

右值:

         const int i;

         clock(10,10,10);

所以 const clock &ret =const(10,10,10)

        ret.settime();

      无法继续进行,settime()需要的是可读可写的左值,我们给的是只读的右值

const clock &ret = clock(10, 10, 10);  
ret.settime(); // 错误:ret是一个指向const的引用,指向一个右值,该右值在表达式结束后会被销毁。

        这里的clock(10, 10, 10)是一个右值,试图将它绑定到一个const引用上。虽然这在语法上是允许的,但由于这个右值在表达式结束后就会被销毁,所以你不能对它进行任何修改操作,即使是通过const引用也不行

(在这里的尝试是调用settime()方法,这显然是想要修改对象)

解决办法

     const    clock  &&ret=clock(10,10,10)

      将其变成了一个右值引用的办法,使其可以引用。

clock &&ret = clock(10, 10, 10);  
ret.settime(); // 正确:ret是一个右值引用,它延长了临时对象的生命周期。

        样,ret就成了一个指向右值的引用,它允许我们修改这个右值,并且由于右值引用的特性,这个右值的生命周期被延长到了与ret相同的生命周期。这就是C++11中引入右值引用的主要目的之一:允许我们更有效地处理临时对象,特别是与移动构造函数和移动赋值运算符相关的场景。

三、类的静态成员

         类的静态成员是类级别(而非实例级别)的共享成员,它属于类本身而不是类的任何特定实例。静态成员包括静态变量(也称为类变量)和静态方法。静态成员的特点是在所有实例之间共享,并且可以在没有创建类实例的情况下通过类名直接访问。没有对象情况下直接就可以调用

静态成员变量的声明方法:

static 数据类型 成员变量名;

#include<iostream>
using namespace std;
class Data
{
    public:
        int n;
        static int m;    //静态成员变量 类内声明
};
int Data::m=0;        //类外初始化

         
int main()
{
    Data d1,d2;
    return 0;
}

// d1.n 与 d2.n 是两个不同的变量

// d1.m 与 d2.m 是同一个变量

静态成员变量的初始化

数据类型 类名::静态成员变量 = 初始值;

#include<iostream>
using namespace std;
class Data
{
    public:
        int n;
        static int m;    //静态成员变量 类内声明
};

int Data::m=0;                    //类外初始化

int main()
{
    Data d1,d2;
    return 0;
}

(一)静态数据成员

在类内数据成员的声明前加上关键字static,该数据成员就是类内的静态数据成员。

    #include <iostream>
     
    using namespace std;
     
    class MyClass
    {
    public:
        MyClass(int a, int b, int c);
        void fun();
    private:
        int a,b,c;
        static int sum;//声明静态数据成员
    };
     
     int MyClass::sum = 0;
    MyClass::MyClass(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
        sum += a+b+c;
    }
     
    void MyClass::fun()
    {
        cout<<"sum = "<<sum<<endl;
    }
    void main()
    {
        MyClass M(1,2,3);
        M.fun();
     
        MyClass N(4,5,6);
        N.fun();
    }


从以上的程序可以看出,静态数据成员有以下特点:
  • 对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。

无论这个类的对象定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。

即静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新。

  • 静态数据成员存储在全局数据区,静态数据成员定义时要分配空间,所以不能在类声明中定义。应该在类外定义。

静态数据成员和普通数据成员一样遵从public、private、protected访问规则。

由于静态数据成员在全局数据区分配内存,属于本类的所有对象共享,它不属于特定的类对象,在没有产生类对象时作用域就可见。即在没有产生类的实例时,我们就可以操作它。

静态数据成员的初始化与一般数据成员的初始化不同,即它的初始化格式为:

<数据类型><类名>::<静态数据成员> = <值>

类的静态数据成员有两种访问方式:

如果静态数据成员的访问权限允许的话,即为public成员,可在程序中,按上述格式来引用静态数据成员;

静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对一个存款类,每个实例的利息都是相同的,所以把利息可以设为存款类的静态数据成员。

这有两个好处,

一是不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省了存储空间。

二是一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了。

同全局变量相比,使用静态数据成员有两个优势:

(1)命名空间问题:静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其他全局名字冲突的可能性;

(2)封装问题:可以实现信息隐藏。静态数据成员可以使private成员,而全局变量不能。

上述例子中修改s1,s2不会跟着变。

其中的static string companyName即静态数据成员

对于类的静态成员的访问可以使用类名+双冒号来访问

做到就算没有成员变量,也可以来访问内部数据。

访问私有成员:

(二)类的静态函数成员

静态成员函数的声明

声明方法:

static 返回值类型函数名(参数表)

//在书写静态成员函数的代码时,不必写static   ## static:声明要,定义不要

#include<iostream>
using namespace std;
class Student
{
    int id;
    float score;
    static float sum;    //总分
    static int count;    //计数
    
    public:
        Student(int i,int s):id(i),score(s)        //定义构造函数
        {
            count++;
        }
        void total();                //总分    
        
        static float average(); //平均数             //静态成员函数的声明
};
        
float Student::sum = 0;        //静态成员变量的初始化
int Student::count = 0;

void Student::total()
{
    sum += score;
}

float Student::average()        //静态成员函数
{
    if(count)
        return sum/count;
}

int main()
{
    Student stu[3] = {Student(1,10) , Student(2,20) , Student(3,30)};
    for(int i=0;i<3;i++)
        stu[i].total();
    cout<<"平均分是:"<<Student::average();        //不需要用对象,因为这是共有的
    return 0;
}

说明:静态成员函数可以直接引用私有的静态数据成员(不需要加类名或者对象名)

建议:只用静态成员函数引用静态数据成员,而不引用非静态数据成员。这会使思路清晰,不易出错

2、静态成员函数

与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部服务,而不是为某一个类的具体对象服务。

静态成员函数与静态数据成员一样,都是在类的内部实现,属于类定义的一部分。

普通的成员函数一般都隐藏了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。

通常情况下,this指针是缺省的、但是与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针,从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数

Example 6

    #include <iostream>
     
    using namespace std;
     
    class MyClass
    {
    public:
        MyClass(int a, int b, int c);
        static void fun();
    private:
        int a,b,c;
        static int sum;//声明静态数据成员
    };
     
     int MyClass::sum = 0;
    MyClass::MyClass(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
        sum += a+b+c;
    }
     
    void MyClass::fun()
    {
        cout<<"sum = "<<sum<<endl;
    }
    void main()
    {
        MyClass M(1,2,3);
        M.fun();
     
        MyClass N(4,5,6);
        N.fun();
        MyClass::fun();//静态成员函数的访问
    }


关于静态成员函数,可以总结以下几点:

(1)出现在类体外的函数不能指定关键字static;

(2)静态成员之间可以互相访问,包括静态成员函数访问静态数据成员和访问静态成员函数

(3)非静态成员函数可以任意地访问静态成员函数和静态数据成员;

(4)静态成员函数不能访问非静态成员函数和非静态数据成员;

(5)由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比,
        速度上会有少许的增长;

(6)调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指调用静态成员函数。

(三)举例子

创建一个类可以统计创建对象的个数。

#include <iostream>  
using namespace std;  class Demo  
{  
public:  Demo()  {  counter++; }  ~Demo()  {  counter--;  }  static int getCounter(); // 修正函数名以匹配变量名  
private:  static int counter; // 修正变量名以匹配构造函数和析构函数中的使用  
};  int Demo::counter = 0; // 显式初始化静态成员变量  int Demo::getCounter()  
{  return counter;  
}  int main(void)  
{  cout << "Before creating instances: " << Demo::getCounter() << endl;  Demo d1, d2, d3;  cout << "After creating 3 instances: " << Demo::getCounter() << endl;  // 当d1, d2, d3超出作用域时,它们的析构函数将被调用  // 但由于main函数结束时整个程序都会结束,所以这里可能不会立即看到析构函数的效果  return 0;  
}

如果不希望有复制构造函数的话,将复制构造函数设为私有。

#include <iostream>  using namespace std;  class Demo {  
public:  Demo() {  counter++; // 构造函数,每次创建新对象时增加计数器  cout << "Constructor called" << endl;  }  // 将复制构造函数设为私有  Demo(const Demo& other) {  cout << "Copy Constructor called" << endl;  // 注意:在实际禁止复制的场景中,这里可能不需要做任何操作,或者抛出一个异常  }  ~Demo() {  counter--; // 析构函数,每次销毁对象时减少计数器  cout << "Destructor called" << endl;  }  static int getCounter();  private:  static int counter;  // 禁止赋值操作(可选,根据需求)  Demo& operator=(const Demo& other) = delete;  
};  int Demo::counter = 0;  int Demo::getCounter() {  return counter;  
}  int main(void) {  cout << "Before creating instances: " << Demo::getCounter() << endl;  Demo d1;  // 由于复制构造函数是私有的,以下行将导致编译错误  // Demo d2 = d1; // 这里会调用复制构造函数,但现在它是私有的  // 如果确实需要类似复制的行为,可以考虑提供一个克隆函数(不是复制构造函数)  cout << "After creating instance: " << Demo::getCounter() << endl;  // 当d1超出作用域时,它的析构函数将被调用  return 0;  
}

四、设计模式其一

目录

一、标识符的作用域和可见性

(一)作用域

1、全局作用域

2.局部作用域

3、类作用域

4、命名空间作用域(Namespace Scope)

(二)可见性

二、生存期

1、静态生存期

2、动态生存期

3、举例

(1)在全局上声明Text 类的全局变量mit;

(2)用static修饰类Test的变量t,使其在整个程序结束时才销毁。

(3)未被初始化的静态生存期会被置零。

(4)匿名对象是动态生存期

三、类的静态成员

(一)静态数据成员

从以上的程序可以看出,静态数据成员有以下特点:

静态数据成员存储在全局数据区,静态数据成员定义时要分配空间,所以不能在类声明中定义。应该在类外定义。

这有两个好处,

同全局变量相比,使用静态数据成员有两个优势:

静态成员函数的声明

2、静态成员函数

关于静态成员函数,可以总结以下几点:

四、设计模式其一

五、常成员函数:

五、友元函数

2,类做友元


23 种设计模式详解(全23种)_23种设计模式-CSDN博客

单例模式实现

      是一种常用的软件设计模式,用于确保一个类仅有一个实例,并提供一个全局访问点来获取该实例。这种模式在需要控制资源访问(如数据库连接、文件句柄等)或者实现配置信息的全局访问时非常有用。

单例模式的实现通常包含以下几个关键要素:

  1. 私有构造函数:防止外部代码通过 new 关键字创建类的实例。

  2. 私有静态变量:用于存储类的唯一实例。

  3. 公有静态方法:提供一个全局访问点来获取类的唯一实例。该方法在第一次被调用时创建实例,并在后续调用时返回已创建的实例。

  4. 确保线程安全(可选):在多线程环境下,需要确保单例的创建过程是线程安全的,以避免多个线程同时创建多个实例。

#include <iostream>  class Singleton {  
public:  static Singleton* getInstance() {  if (m_p == NULL) {  m_p = new Singleton;  }  return m_p;  }  static void destroyInstance() {  delete m_p;  m_p = NULL;  }  static int getCounter() {  return counter;  }  private:  Singleton() {  counter++;  }  ~Singleton() {  counter--;  }  static Singleton* m_p;  static int counter;  
};  Singleton* Singleton::m_p = NULL;  
int Singleton::counter = 0;  int main(void) {  Singleton* p = Singleton::getInstance();  std::cout << Singleton::getCounter() << std::endl;  // 应该输出 1  Singleton* q = Singleton::getInstance();  std::cout << p << std::endl;  std::cout << q << std::endl;  // 如果两个地址相同,则说明是同一个实例  // 注意:在实际应用中,您可能希望在程序结束前调用 destroyInstance()  // 但在这个简单的示例中,我们省略了这一步,因为当程序结束时,操作系统会回收所有资源  return 0;  
}

五、常成员函数:

在C++中,常成员函数(也称为常量成员函数)是一种特殊的成员函数,它承诺不会修改类的任何成员变量(静态成员变量除外,因为它们不属于类的任何特定对象)。常成员函数通过在函数声明后加const关键字来标识。

1.常成员函数声明:
const成员函数也就是常成员函数,它的声明形式:

返回类型 成员函数名(参数表) const ;
例如: int function(int x) const ;

易混淆的地方:

const 的位置:
函数开头的 const 用来修饰函数的返回值,表示返回值是 const 的,也就是不能被修改,
例如const char * getname()
函数头部的结尾加上 const 表示常成员函数,这种函数只能读取成员变量的值,而不能修改成员变量的值,例如char * getname() const。

2.常成员函数的主要特点:
1)不能更新类的成员变量
2)不能调用该类中没有用const修饰的成员函数,即只能调用常成员函数
3)可以被类中其它的成员函数调用
4)常对象只能调用常成员函数,而不能调用其他的成员函数

例如在clock类中:

显示时间得函数是不需要修改任何参数得。

 


 下面是另外一个例子:

class CDate
{public://常成员函数int year() const{ return y; }int month() const{//m++;              //错误:1)常成员函数中不能更新类的数据成员return m;}//普通成员函数int day(){return d++ ;}int AddYear(int i){ return y+1; }private:int y;int m;int d;
};int main()
{CDate const date;         //常对象//int day = date.day();   // 4)错误常对象不能调用非常成员函数int year = date.year();   //正确
}
class A{
private:int w,h;public:int getValue() const{           //常成员函数return w*h + getValue2();   //2)错误。只能调用常成员函数。}int getValue2(){		     	//普通成员函数return w+h+getValue();      //3)正确。常成员函数可以被其它成员函数调用}A(int x,int y){w=x,h=y;}A(){}};

六、友元函数

参考:https://blog.csdn.net/weixin_46098577/article/details/116596183

生活中你的家有客厅(public),有你的卧室(private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去

但是呢,你也可以允许 隔壁老王 进去。

在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术

友元的目的 就是让一个函数或者类 访问另一个类中的私有成员

友元的关键字为 friend

友元的三种实现

全局函数做友元
类做友元
成员函数做友元

(1)全局函数做友元

首先,我们要定义一个房屋类,公共成员变量为客厅,私有成员变量为卧室

class Building
{
    // Building的构造函数,给成员变量赋初值
    Building()
    {
        m_SittingRoom = "客厅";
        m_BedRoom = "卧室";
    }
    
    string m_SittingRoom;    // 客厅

private:

    string m_BedRoom;        // 卧室
};
 

然后定义一个全局函数 laoWang(),用来访问Building类中的私有成员

void laoWang1(Building *building)
{
    cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_SittingRoom << endl;

    cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_BedRoom << endl;
}
 

 当然也可以用引用传递或者最简单的值传递

void laoWang2(Building &building)
{
    cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_SittingRoom << endl;

    cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_BedRoom << endl;
}

void laoWang3(Building building)
{
    cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_SittingRoom << endl;

    cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_BedRoom << endl;
}
 

最后定义一个测试函数test(),实现 laoWang() 这个全局函数做友元访问类的私有成员

void test()
{
    Building building;
    laoWang1(&building);
    laoWang2(building);
    laoWang3(building);
}
 

但是,现在还不能实现全局函数访问类的私有成员!

关键代码

friend void laoWang1(Building *building);
friend void laoWang2(Building &building);
friend void laoWang3(Building building);
 

在Building类中声明友元函数,告诉编译器 laoWang全局函数是 Building类 的好朋友,可以访问Building对象的私有成员

#include <iostream>
#include <string>

using namespace std;

// 房屋类
class Building
{
    // 告诉编译器 laoWang全局函数是 Building类  的好朋友,可以访问Building对象的私有成员
    friend void laoWang1(Building *building);
    friend void laoWang2(Building &building);
    friend void laoWang3(Building building);

public:

    Building()
    {
        m_SittingRoom = "客厅";
        m_BedRoom = "卧室";
    }
    
    string m_SittingRoom;    // 客厅

private:

    string m_BedRoom;        // 卧室
};

//全局函数
void laoWang1(Building *building)
{
    cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_SittingRoom << endl;

    cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_BedRoom << endl;
}

void laoWang2(Building &building)
{
    cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_SittingRoom << endl;

    cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_BedRoom << endl;
}

void laoWang3(Building building)
{
    cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_SittingRoom << endl;

    cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_BedRoom << endl;
}

void test()
{
    Building building;
    laoWang1(&building);
    laoWang2(building);
    laoWang3(building);
}


int main()
{
    test();
}
 

(2)类做友元

首先,声明一个要访问的私有变量所属的Building类,防止在下面的好LaoWang类中,编译器不认识Building(当然也可以采取先定义Building类,再定义隔壁老王LaoWang类,这样就不用声明Building类了)

#include <iostream>
#include <string>

using namespace std;

// 类作友元

class Building;
class LaoWang
{
public:

    LaoWang();

    void visit();    //参观函数  访问Building中的属性

    Building * building;


private:


};

// 房屋类
class Building
{
    // 告诉编译器,LaoWang类是Building类的好朋友,可以访问Building类的私有成员
    friend class LaoWang;
public:
    
    Building();
        
    string m_SittingRoom;    // 客厅
    
private:
    
    string m_BedRoom;        // 卧室
};

// 类外定义成员函数

Building::Building()
{
    m_SittingRoom = "客厅";
    m_BedRoom = "卧室";
}

LaoWang::LaoWang()
{
    // 创建建筑物对象
    building = new Building;
}

void LaoWang::visit()
{
    cout << "隔壁老王LaoWang类正在访问:" << building->m_SittingRoom << endl;

    cout << "隔壁老王LaoWang类正在访问:" << building->m_BedRoom << endl;
}

void test()
{
    LaoWang lw;
    lw.visit();
}

int main()
{
    test();

    return 0;
}
 

3 成员函数做友元

#include <iostream>
#include <string>

using namespace std;

class Building;

class LaoWang
{
public:

    LaoWang();
    void visit1();    //让visit1()函数   可以 访问Building中的私有成员
    void visit2();    //让visit2()函数 不可以 访问Building中的私有成员

    Building *building;

private:

    
};

class Building
{
    // 告诉编译器,LaoWang类下的visit1()函数是Building类的好朋友,可以访问Building的私有成员
    friend void LaoWang::visit1();

public:
    Building();

    string m_SittingRoom;    //客厅
private:

    string m_BedRoom;        //卧室
};


LaoWang::LaoWang()
{
    building = new Building;
}

void LaoWang::visit1()
{
    cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_SittingRoom << endl;
    cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_BedRoom << endl;
}

void LaoWang::visit2()
{
    cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_SittingRoom << endl;
    //cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_BedRoom << endl;    //错误!私有属性不可访问
}

Building::Building()
{
    m_SittingRoom = "客厅";
    m_BedRoom = "卧室";
}

void test()
{
    LaoWang lw;
    
    lw.visit1();

    lw.visit2();
}

int main()
{
    test();
    
    return 0;
}
 

七 volatile禁止优化关键字

       在C++中,volatile关键字确实用于指示编译器该变量的值可能会以编译器无法预见的方式被改变。这主要用于访问硬件寄存器、内存映射的I/O地址、多线程程序中被其他线程修改的共享数据,或者在中断服务例程(ISR)中可能被修改的变量等场景。使用volatile关键字可以阻止编译器对这些变量进行优化,如缓存变量的值或对其进行不必要的重排序。

     第一个函数中,变量i被声明为一个整型常量,并赋值为100。然后,p变量被初始化为指向i的指针,并设置为1000。接下来,在第二个函数中,同样声明了变量li并赋值为100。
然而,这里的p变量被初始化为指向li的指针,并设置为1000。
           最后,两个函数都使用了cout来输出变量i的值。需要注意的是,虽然这两个函数的输入参数是相同的,但它们的结果不同。因为第一个函数返回的是原始的i值,而第二个函数返回的是通过指针访问到的li值。

八、强制类型转换

static_cast

  • 用于在具有明确转换关系(如基类和派生类之间的转换、基本数据类型之间的转换等)的类型之间进行转换。
  • 可以在一定程度上提供编译时检查,增加代码的安全性。
  • 示例:将派生类指针或引用转换为基类指针或引用(向上转换)或基类指针转换为派生类指针(向下转换,但不安全,除非已经通过其他方式(如dynamic_cast)确认了类型)。

class Base {  
public:  virtual ~Base() {}  void show() { std::cout << "Base" << std::endl; }  
};  class Derived : public Base {  
public:  void display() { std::cout << "Derived" << std::endl; }  
};  int main() {  Derived* d = new Derived();  Base* b = static_cast<Base*>(d); // 向上转换,安全  b->show(); // 输出: Base  delete d;  
}

reinterpret_cast

  • 几乎可以转换任何类型的指针(或引用)到其他类型的指针(或引用),甚至包括不相关的类型。
  • 它主要用于底层编程和与C语言代码的互操作,如处理函数指针或转换指针类型以符合特定硬件要求。
  • 几乎不进行类型检查,因此使用时需要格外小心。
class Base {  
public:  virtual ~Base() {}  void show() { std::cout << "Base" << std::endl; }  
};  class Derived : public Base {  
public:  void display() { std::cout << "Derived" << std::endl; }  
};  int main() {  Derived* d = new Derived();  Base* b = static_cast<Base*>(d); // 向上转换,安全  b->show(); // 输出: Base  delete d;  
}

const_cast:
去原来的const属性

  • 用于去除类型的constvolatileconst volatile限定符。
  • 通常用于修改由函数返回的常量指针或引用所指向的数据(注意,这只有在你确定这样做是安全的情况下才应该进行)。
  • 示例:将const int*转换为int*以修改所指向的值。
  • void modifyConstInt(const int* ptr) {  int* nonConstPtr = const_cast<int*>(ptr); // 去除const属性  *nonConstPtr = 20; // 修改值,注意这里需要确保原始数据不是const的  
    }  int main() {  int value = 10;  modifyConstInt(&value); // 传递非const变量的地址  std::cout << value << std::endl; // 输出: 20  
    }

dynamic_cast

  • 主要用于类的层次结构中的安全向下转换(将基类指针或引用转换为派生类指针或引用)。
  • 在运行时检查转换的安全性,如果转换不合法,则转换失败(对于指针,转换结果为nullptr;对于引用,转换失败会抛出std::bad_cast异常)。
  • 适用于含有虚函数的类之间的转换,因为dynamic_cast依赖于运行时类型信息(RTTI)。
  • class Base {  
    public:  virtual ~Base() {}  virtual void show() { std::cout << "Base" << std::endl; }  
    };  class Derived : public Base {  
    public:  void display() { std::cout << "Derived" << std::endl; }  
    };  int main() {  Base* b = new Derived();  Derived* d = dynamic_cast<Derived*>(b); // 向下转换,运行时检查  if (d) {  d->display(); // 输出: Derived  }  delete b;  
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397743.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

X射线物质质量衰减系数的查询计算方法

最近进行硕士毕业课题&#xff0c;需要各种各样物质的质量衰减系数&#xff08;线性衰减系数&#xff09;&#xff0c;包括高原子序数的金属物质还有一些复杂的化合物或者混合物&#xff0c;之前知道美国的XCOM &#xff1a;XCOM: Photon Cross Sections Database这个数据库可以…

仓颉语言运行时轻量化实践

杨勇勇 华为语言虚拟机实验室架构师&#xff0c;目前负责仓颉语言静态后端的开发工作 仓颉语言运行时轻量化实践 仓颉Native后端&#xff08;CJNative&#xff09;是仓颉语言的高性能、轻量化实现。这里的“轻量化”意指仓颉程序运行过程中占用系统资源&#xff08;内存、CPU等…

dll修复工具有没有免费的?排行榜Top8更新,一键修复所有dll缺失

DLL 错误是常见的系统问题&#xff0c;可能导致系统崩溃或 Windows 故障&#xff0c;这让每天使用电脑的人倍感烦恼。为了有效解决这些反复出现的问题&#xff0c;使用 DLL 修复工具显得尤为重要。对于喜欢尝试免费软件的用户&#xff0c;市面上有许多优秀的免费dll 修复工具可…

打开 Mac 触控板的三指拖移功能

对于支持力度触控的触控板&#xff0c;可以选择使用三指手势来拖移项目。 相应的设置名称会因你使用的 macOS 版本而有所不同&#xff1a; 选取苹果菜单  >“系统设置”&#xff08;或“系统偏好设置”&#xff09;。 点按“辅助功能”。 点按“指针控制”&#xff08;…

【vue3】【elementPlus】【国际化】

1.如需从0-1开始&#xff0c;请参考 https://blog.csdn.net/Timeguys/article/details/140995569 2.使用 vue-i18n 模块&#xff1a; npm i vue-i18n3.在 src 目录下创建 locales 目录&#xff0c;里面创建文件&#xff1a;en.js、zh-cn.js、index.js 语言js文件&#xff1a;…

html5宠物网站模板源码

文章目录 1.设计来源1.1 主界面1.2 主界面菜单1.3 关于我们界面1.4 宠物照片墙界面1.5 宠物博客界面1.6 宠物服务界面1.7 宠物团队界面1.8 联系我们界面 2.效果和源码2.1 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 【博主推荐】&a…

【轻松掌握】使用Spring-AI轻松访问大模型本地化部署并搭建UI界面访问指南

文章目录 读前必看什么是Spring-AI目前已支持的对接模型本文使用Spring-AI版本构建项目选择必要的依赖配置系统变量 聊天模型API配置文件方式1-使用默认配置方式2-自定义配置配置其他参数使用示例 图像模型API配置文件方式1-使用默认配置方式2-自定义配置配置其他参数使用示例 …

开发效率翻倍攻略!大学生电脑小白管理秘籍,资料秒搜技巧大公开!C盘满了怎么办?如何快速安全的清理C盘?烦人的电脑问题?一键解决!

如何正确管理自己的第一台电脑&#xff1f;大一新生如何管理自己的电脑&#xff1f;老鸟如何追求快捷操作电脑&#xff1f; 文章目录 如何正确管理自己的第一台电脑&#xff1f;大一新生如何管理自己的电脑&#xff1f;老鸟如何追求快捷操作电脑&#xff1f;前言初级基础分区操…

2024年第八届计算生物学与生物信息学国际会议 (ICCBB 2024)即将召开!

2024 年第八届计算生物学和生物信息学国际会议&#xff08;ICCBB 2024&#xff09;将于2024年11月28 -30在日本京都召开&#xff0c;ICCBB 2024是展示理论、实验和应用计算生物学和生物信息学领域新进展和研究成果的主要论坛之一。我们相信&#xff0c;通过大家的共同努力&…

oled使用 f4软件iic 数字 汉字 小图片 HAL库

基于江科大的oled标准库进行移植 到Hal库上 本人参考了许多大佬的源码 进行更改 由于F4和F1主频不一样 由于F4主频太高 在进行软件iic时需要延时一下 才可驱动oled 本人在网上找了一个开源的us延时函数 已经添加进入 文件分享 通过百度网盘分享的文件&#xff1a;delay&#…

记一次 .NET某智慧出行系统 CPU爆高分析

一&#xff1a;背景 1. 讲故事 前些天有位朋友找到我&#xff0c;说他们的系统出现了CPU 100%的情况&#xff0c;让你帮忙看一下怎么回事&#xff1f;dump也拿到了&#xff0c;本想着这种情况让他多抓几个&#xff0c;既然有了就拿现有的分析吧。 二&#xff1a;WinDbg 分析…

socket回显服务器练习

前言 什么是回显服务器(echo server)呢&#xff1f; 回显服务器接收客户端发送的任何数据&#xff0c;并将这些数据原封不动地发送回客户端。回显服务器在连接成功的基础上只需要知道如何在客户端将收到的信息打印输出到控制台即可。我接下来会使用两种方法来输出&#xff0c;…

精通C++ STL(六):list的模拟实现

目录 类及其成员函数接口总览 结点类的模拟实现 构造函数 迭代器类的模拟实现 迭代器类存在的意义 迭代器类的模板参数说明 构造函数 运算符的重载 --运算符的重载 运算符的重载 !运算符的重载 *运算符的重载 ->运算符的重载 list的模拟实现 默认成员函数 构造函数 拷贝…

力扣 | 递增子序列 | 动态规划 | 最长递增子序列、最长递增子序列的个数、及其变式

文章目录 一、300. 最长递增子序列二、673. 最长递增子序列的个数三、变式1、646. 最长数对链2、1218. 最长定差子序列3、1027. 最长等差数列4、354. 俄罗斯套娃信封问题5、1964. 找出到每个位置为止最长的有效障碍赛跑路线 最长递增子序列&#xff1a;原序-递增数值问题 最长定…

无人机之陀螺仪篇

陀螺仪器最早是用于航海导航&#xff0c;但随着科学技术的发展&#xff0c;它在航空和航天事业中也得到广泛的应用。陀螺仪不仅可以作为指示仪表&#xff0c;而更重要的是它可以作为自动控制系统中的一个敏感元件&#xff0c;即可作为信号传感器。 根据需要&#xff0c;陀螺仪器…

使用字典优化命令行参数处理

在 Python 中&#xff0c;可以使用字典优化命令行参数的处理。特别是在处理多个选项或参数时&#xff0c;使用字典可以使代码更加简洁和易于维护。以下是一个使用 argparse 模块和字典来管理命令行参数的示例。 问题背景 在一个项目中&#xff0c;您需要根据用户通过命令行参数…

yolov5更换MobileNetV3的主干网络

目录 1.定义网络 2.将test_MobileNetV3.py上面的代码复制粘贴到如下文件里面 2.1复制需要更改的原来的网络如何改结构 3.更改yolo.py注册网络 1.定义网络 1. 宽度为0.5的YOLOV5网络的结构图 在主干网络上面可以重新定义成三层&#xff0c;编号从0开始 如图是MobileNetV3 的…

掌握7步网站设计流程,提升你的网站用户体验

在当代社会&#xff0c;网站制作已经成为不可或缺的一部分。随着互联网的发展&#xff0c;越来越多的人开始使用网站浏览器来获取信息、交流和消费。一个好的网站设计可以吸引用户的注意力&#xff0c;提高用户体验&#xff0c;从而提高用户的满意度和忠诚度。那么&#xff0c;…

网络剪枝——network-slimming 项目复现

目录 文章目录 目录网络剪枝——network-slimming 项目复现clone 存储库Baselinevgg训练结果 resnet训练结果 densenet训练结果 Sparsityvgg训练结果 resnet训练结果 densenet训练结果 Prunevgg命令结果 resnet命令结果 densenet命令结果 Fine-tunevgg训练结果 resnet训练结果 …

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——5.string

1.字符串相乘 . - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 1.如果两个串有一个首元素为‘0’&#xff0c;则直接返回‘0’ 2.设置两层循环&#xff0c;内层第一次循环 用于str插入初始数据 &#xff08;num2 的各个元素和num1 的最后一个元素相乘的结果&#…