首页 > 编程语言 >C++基础杂记(2)

C++基础杂记(2)

时间:2023-10-31 13:35:16浏览次数:37  
标签:初始化 const name int age 基础 C++ 杂记 Baby



将数组传入函数

void test(int array[]);
void test(int* array);

两种传递方式完全等价,但是第一种传递方式的可读性更高。

禁止修改数组的值

void test(const int array[]);
void test(const int* array);

注意,const 能用则用,如果声明函数时。函数的参数是带 const 的,则函数可以接受带 const 或不带 const 的参数。若函数参数不带 const,则只能接受不带 const 的参数。

函数的地址与函数的指针

#include <iostream>
using namespace std;

void myPrint()
{
        cout<< "myPrint\n";
        return;
}

void getResult(void (*printMethod)(void))   // 函数的参数为函数指针/地址
{
        (*printMethod)();           // printMethod(); 同样有效
        return;
}

int main(int argc, char** argv)
{
        void (*myPrintPtr)(void);   // 函数指针
        myPrintPtr = myPrint;       // 将函数地址赋值给函数指针

        cout<< "myPrint 函数地址:"<< (void*)myPrint<< endl;        // 由于cout没有输出函数地址类型的重载,因此需要显示的将函数地址声明为指针类型才能有效输出。否则会默认输出boolean值
        cout<< "myPrint 函数指针:"<< (void*)myPrintPtr<< endl;     // 由于cout没有输出函数指针类型的重载,因此需要显示的将函数指针声明为指针类型才能有效输出。否则会默认输出boolean值
        cout<< "myPrint 函数指针地址:"<< &myPrintPtr<< endl;

        getResult(myPrint);         // 可以接受函数地址
        getResult(myPrintPtr);      // 可以接受函数指针
        return 0;
}

myPrint 函数地址:0x55b39f80097a
myPrint 函数指针:0x55b39f80097a
myPrint 函数指针地址:0x7ffd48cd26b0
myPrint
myPrint

注意,下列示例中,直接将函数声明为指针是不合法的:

void (*myPrint)()
{
       cout<< "myPrint\n";
       return;
}

函数的指针只是一个指针,不能像函数一样链接代码块。

函数的指针数组

void (*ptr[3])(void) = {func1, func2, func3};   // 定义一个包含三个函数地址的函数指针数组

上述代码可以使用 typedef 简化:

typedef void(*void_ptr)(void);
void_ptr pa[3] = {func1, func2, func3};

函数的 static 与 inline

  • static 静态函数。限制函数的作用域为本文件,不能够被其他文件调用
  • inline 内联函数。建议编译器将函数体嵌入调用他的地方,类似于宏定义的替换。这样可以使得函数在被调用时,程序不需要跳转到函数处,可以提高效率,但占用内存。inline 修饰的函数不能够先声明后定义,只能够直接进行定义。

引用左值和引用传参

  • 当将临时变量赋给引用时,该引用需要被const修饰
    double a = 10.0;
    const int & b = (int)a;   // (int)a 产生临时变量,一般来讲,右值表达式都将产生临时常量。临时常量无法被寻址,在左值处加入 const 修饰会让编译器创建一个无名常量记录临时常量的信息,该无名常量可以被寻址,但不可以被改变。
    
  • 以普通引用作为形参的函数不能接收常量作为实参传递
    #include <iostream>
    using namespace std;
    void test(int & a);
    int main()
    {
        const int a = 10;   // 常量
        test(a);            // 将常量传递给普通引用函数
        return 0;
    }
    void test(int & a)
    {
        cout<< a<< endl;
    }
    

    报错:test.cpp:10:8: error: binding reference of type ‘int&’ to ‘const int’ discards qualifiers

  • 当函数以常量引用作为形参时,可以接受非常量传参
  • 更多细节参考文章

C++11 的数组 for 循环

int a[] = {1, 2, 3, 4};
for(int b:a)
{
    cout<< b<< " ";
    ++b;
}
cout<< endl;

for(int b:a)
{
    cout<< b<< " ";
}
cout<< endl;

1 2 3 4
1 2 3 4
这里第一个for循环中的数组a中的每一个值相当于按值传递给b,b的地址是唯一的且不指向a数组中的值。修改b无法修改a数组。

int a[] = {1, 2, 3, 4};
for(int & b:a)
{
    cout<< b<< " ";
    ++b;
}
cout<< endl;

for(int b:a)
{
    cout<< b<< " ";
}
cout<< endl;

1 2 3 4
2 3 4 5
想要修改a数组,则需要在第一个for循环中,让a数组中的值传引用到b,此时b指向a数组中的值,修改b可以修改a数组。

64 位 Linux 操作系统中 C++ 中常见基本类型所占字节数

pointer: 8字节
char : 1字节
unsigned char : 1字节
char16_t : 2字节
short : 2字节
unsigned short : 2字节
char32_t : 4字节
w_char : 4字节
int : 4字节
unsigned int : 4字节
float : 4字节
long : 8字节
long long : 8字节
unsigned long (long) : 8字节
double : 8字节
long double : 16字节

字节数比指针大的,声明函数时按参传递会有优势。

C++11 类成员变量的初始化

默认成员初始化器

在声明变量时直接初始化

class Baby
{
public:	
	Baby(const std::string name) : _name_(name) {}
	Baby(int age) : _age_(age) {}
	
private:
	// 默认成员初始化, 在声明处就进行初始化。
	std::string _name_ = "Unknown";
	int _age_ = 0;
	int _weight_ = 0;
	int _height_ = 0;
};

成员变量初始化列表

class Baby
{
public:	
	// 采取成员初始化列表方式,大量成员变量在多个初始化列表中重复出现
	Baby(const std::string name):_name_(name), _age_(0), _weight_(0), _height_(0){}
	Baby(int age) :_name_("Unknown"), _age_(age), _weight_(0), _height_(0) {}
	// ...省略100个构造函数

	// 采取先默认构造,再在构造函数中赋值,有机会去除这类重复代码。
	Baby(const std::string name){
		init(name, 0, 0, 0);
	}
	Baby(int age){
		init("Unknown", age, 0, 0);
	}
	// ...省略100个构造函数

	void init(const std::string& name, int age, int weight, int height)
	{
		_name_ = name;
		_age_ = age;
		_weight_ = weight;
		_height_ = height;
	}

private:
	std::string _name_;
	int _age_;
	int _weight_;
	int _height_;
};

传统的,在构造函数函数体里面进行初始化,实际上是先将变量初始化为0再赋值。而列表初始化,是直接在为变量分配内存时就初始化为对应的值。效率更高。

委托构造函数

如果构造函数需要更多的重构,写起来会很麻烦,C++11 允许我们将这些重构委托给一个构造函数

class Baby
{
public:	
	Baby(const std::string& name, int age, int weight, int height) :
		_name_(name), _age_(age), _weight_(weight), _height_(height) {}
	Baby(const std::string name) : Baby(name, 0, 0, 0) {}	// 委托构造函数方式
	Baby(int age) : Baby("Unknown", age, 0, 0) {}	// 委托构造函数方式
	// ...省略100个构造函数

private:
	std::string _name_;
	int _age_;
	int _weight_;
	int _height_;
};

定义对象时使用列表初始化

在 C++11 中,类可以如同结构体一般,在定义对象时使用列表进行初始化。前提是我们的构造函数中有相对应的方法。

class Baby
{
public:	
	Baby(const std::string& name, int age, int weight, int height) :
		_name_(name), _age_(age), _weight_(weight), _height_(height) {}

private:
	std::string _name_;
	int _age_;
	int _weight_;
	int _height_;
};

int main()
{
    Baby baby = {"name", 10, 10, 10};   // 复制列表初始化
    Baby baby {"name", 10, 10, 10};     // 列表初始化
}

当构造函数没有被 explicit 修饰时,复制列表初始化与列表初始化都是合法的,但是当构造函数被 explicit 修饰时,被修饰的构造函数的类,不能发生相应的隐式类型转换,此时最好使用列表初始化而不使用复制列表初始化。

另外,类中成员变量的初始化顺序取决于他们在类中被声明的顺序。

友元可以访问类的私有变量

class Baby
{
private:
    std::string baby_name;
    int baby_age;
    double baby_weight;
public:
    Baby(const std::string & _baby_name, const int _baby_age, const double _baby_weight):baby_name(_baby_name), baby_age(_baby_age), baby_weight(_baby_weight){}
    inline void showName() const {std::cout<<"Name: "<<baby_name<<std::endl;}
    inline void showAge() const {std::cout<<"Age: "<< baby_age<<std::endl;}
    inline void showWeight() const {std::cout<<"Weight: "<<baby_weight<<std::endl;}
    inline void showAll() const {showName();showAge();showWeight();}
    friend std::ostream & operator<<(std::ostream & os, const Baby & baby); // 友元声明
};

std::ostream & operator<<(std::ostream & os, const Baby & baby) // 友元定义
{
    os<<"Name: "    <<baby.baby_name    <<std::endl     // 友元使用了私有变量
      <<"Age: "     <<baby.baby_age     <<std::endl     // 友元使用了私有变量
      <<"Weight: "  <<baby.baby_weight  <<std::endl;    // 友元使用了私有变量

    return os;
}

int main()
{
    Baby baby = {"name", 2, 15};
    baby.showAll();                 // 打印全部信息
    std::cout<<baby;                // 打印全部信息
    return 0;
}

示例中,类可以通过他自己的公共方法 showAll 来打印信息,也可以通过友元重载 << 运算符来打印信息。在友元的定义中,我们访问了类的私有变量。友元可以访问类的私有变量。

标签:初始化,const,name,int,age,基础,C++,杂记,Baby
From: https://www.cnblogs.com/torch-hxm/p/17800040.html

相关文章

  • C++基础杂记(3)
    类的继承基类与派生类之间的构造行为在派生类中使用基类方法protected的访问权限多态公有继承关键字virtual示例抽象基类(ABC)私有继承和保护继承多重继承类的继承基类与派生类之间的构造行为派生类可以调用基类的公共成员,但无法调用基类的私有成员。所......
  • 【面试专题】Java核心基础篇②
    目录1.接口和抽象类有什么区别?2.两个对象的hashCode()相同,则equals()也一定为true,对吗?3.说一说hashCode()和equals()的关系4.为什么要重写hashCode()和equals()?5.==和equals()有什么区别?6.简单聊聊什么是深拷贝?什么是浅拷贝?7.简单讲讲你对Java中的异常的理解?8.遇到过异常吗,如......
  • 10月31日线程基础
    目录线程基础线程的概念如果把操作系统当成一个工厂进程之间是竞争关系,线程之间是什么关系纠正概念进程与线程的区别是什么?线程基础线程的概念在操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程线程顾名思义,就是一条流水线工作的过程,可以这么想一条流水线必须属于......
  • 继续更新完善:C++ 结构体代码转MASM32代码
    一、需求分析在用MASM32编程更新完善SysInfo的网络连接信息,用到了MIB_TCP6TABLE_OWNER_MODULE结构体:typedefstruct_MIB_TCP6TABLE_OWNER_MODULE{DWORDdwNumEntries;MIB_TCP6ROW_OWNER_MODULEtable[ANY_SIZE];}MIB_TCP6TABLE_OWNER_MODULE,*PMIB_T......
  • 神经网络基础篇:史上最详细_详解计算图(Computation Graph)
    计算图可以说,一个神经网络的计算,都是按照前向或反向传播过程组织的。首先计算出一个新的网络的输出(前向过程),紧接着进行一个反向传输操作。后者用来计算出对应的梯度或导数。计算图解释了为什么用这种方式组织这些计算过程。在这个博客中,将举一个例子说明计算图是什么。让举一个比......
  • Opencascad开发(C++)-数据类型转换-Shape、Verterx和gp_pnt的转化
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录1、前言2、TopoDS_Shape与TopoDS_Vertex的转换2.1TopoDS_Shape到TopoDS_Vertex2.2TopoDS_Vertex到TopoDS_Shape3、TopoDS_Vertex与gp_Pnt3.1TopoDS_Vertex到gp_Pnt3.2gp_Pnt到TopoDS_Vertex1、前言在Open......
  • Opencascad(C++)-建模-创建有界直线段
    文章目录1、前言2、用gp_Lin创建一条直线2.1gp_Lin类成员函数2.2创建一条直线2.3运行结果3、创建一条有界的直线段3.1功能说明3.2函数说明3.2创建直线段的代码3.3测试效果1、前言在Opencascad开发时,经常会遇到创建直线的情况,采用gp_Line创建的直线段是无界的,如果想创建......
  • tdc++.so.6: version `GLIBCXX_3.4.29' not found
     001、python程序报错如下: 002、问题分析a、调用的是python程序b、libstdc++.so.6是c++标准库执行python程序时,需要调用c++标准库,libstdc++.so.6(lib=glib,6表示第6版),版本不匹配报错,无法找到:GLIBCXX_3.4.29。 003、确认调用的哪里的python程序(base)[b20223040......
  • c++ ftp上传下载
    #include<afxinet.h>intmain(){BOOLdRes,pRes;HINTERNEThInternet;HINTERNEThConnect;hInternet=InternetOpen("A3GSSample",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,INTERNET_FLAG_NO_CACHE_WRITE);if(NULL==hInternet){printf("Inter......
  • 云计算基础搭建-centOS7和VMware17
    软件:centOS7和VMware171、安装centOS2、查看机器名:hostnamectl3、修改机器名:hostnamectl set-hostname Controller_1  将机器名修改为Controller_14、添加IP地址   首先,查看虚拟机菜单:“编辑”-“虚拟网络编辑器”,查看NAT模式的子网,如:192.168.190.0 子......