0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Modern C++ 有哪些能真正提升开发效率的语法糖

Linux爱好者 来源:程序员小方 作者: 张小方 2021-11-09 11:00 次阅读

我们说的 Modern C++,一般指的是 C++11 及以后的标准,从 C++ 11 开始,Modern C++ 引入了大量的实用的特性,主要是两大方面,学习的时候也可以从这两大方面学习:

  1. 增强或者改善的语法特性;

  2. 新增的或者改善的 STL 库。

我们来看几个具体的案例:

案例 1:统一的类成员初始化语法与 std::initializer_list

在 C++98/03 中,假设我们要初始化一个类数组类型的成员(例如常用的清零操作),我们需要这么写:

classA
{
public:
A()
{
//初始化arr
arr[0]=0;
arr[1]=0;
arr[2]=0;
arr[3]=0;
}

public:
intarr[4];
};

假设数组 arr 较长,我们可以使用循环或者借助 memset 函数去初始化,代码如下:

classA
{
public:
A()
{
//使用循环初始化arr
for(inti=0;i< 4; i++)
             arr[i] = 0;
     }
 
 public:
     int arr[4];
 };
 
 class A
 {
 public:
     A()
{
//使用memset初始化arr
memset(arr,0,sizeof(arr));
}

public:
intarr[4];
};

但是,我们知道,在 C++98/08 中我们可以直接通过赋值操作来初始化一个数组的:

intarr[4]={0};

但是对于作为类的成员变量的数组元素,C++98/03 是不允许我们这么做的。到 C++11 中全部放开并统一了,在 C++11 中我们也可以使用这样的语法是初始化数组:

classA
{
public:
//在C++11中可以使用大括号语法初始化数组类型的成员变量
A():arr{0}
{
}

public:
intarr[4];
};

如果你有兴趣,我们可以更进一步:在 C++ 98/03 标准中,对类的成员必须使用 static const 修饰,而且类型必须是整型 (包括 bool、 char、 int、 long 等),这样才能使用这种初始化语法:

//C++98/03在类定义处初始化成员变量
classA
{
public:
//T的类型必须是整型,且必须使用staticconst修饰
staticconstTt=某个整型值;
};

在 C++11 标准中就没有这种限制了,我们可以使用花括号(即{})对任意类型的变 量进行初始化,而且不用是 static 类型:

//C++11在类定义处初始化成员变量
classA
{
public:
//有没有一种Java初始化类成员变量的即视感^_^
boolma{true};
intmb{2019};
std::stringmc{"helloworld"};
};

当然,在实际开发中,建议还是将这些成员变量的初始化统一写到构造函数的初始化列表中,方便阅读和维护代码。

案例 2:注解标签

C++ 14 引入了[[deprecated]]标签来表示一个函数或者类型等已被弃用,在使用这些被弃用的函数或者类型并编译时, 编译器会给出相应的警告, 有的编译器直接生成编译错误:

[[deprecated]]voidfuncX();

这个标签在实际开发中非常有用,尤其在设计一些库代码时,如果库作者希望某个函数或者类型不想再被用户使用,则可以使用该标注标记。当然,我们也可以使用如下语法给出编译时的具体警告或者出错信息

[[deprecated("usefunYinstead")]]voidfuncX();

有如下代码:

#include
[[deprecated("usefuncYinstead")]]voidfuncX()
{
//实现省略
}

intmain()
{
funcX();
return0;
}

若在 main 函数中调用被标记为 deprecated 的函数 funcX,则在 gcc/g++7.3 中编译时会得到如下警告信息:

[root@myaliyuntestmybook]#g++-g-otest_attributestest_attributes.cpp
test_attributes.cpp:Infunction‘intmain()’:
test_attributes.cpp11:warning:‘voidfuncX()’isdeprecated:usefuncYinstead
[-Wdeprecated-declarations]
funcX();
^
test_attributes.cpp42:note:declaredhere
[[deprecated("usefuncYinstead")]]voidfuncX()

Java 开发者对这个标注应该再熟悉不过了。在 Java 中使用@Deprecated 标注可以达到同样的效果,这大概是 C++标准委员“拖欠”广大 C++开发者太久的一个特性吧。

C++ 17 提供了三个实用注解:[[fallthrough]]、[[nodiscard]]和[[maybe_unused]],这里 逐一介绍它们的用法。[[fallthrough]]用于 switch-case 语句中,在某个 case 分支执行完毕后如果没有 break 语句,则编译器可能会给出一条警告。但有时这可能是开发者有意为之的。为了让编译器明确知道开发者的意图,可以在需要某个 case 分支被“贯穿”的地方(上一个 case 没有break 语句)显式设置 [[fallthrough]] 标记。代码示例如下:

switch(type)
{
case1:
func1();
//这个位置缺少break语句,且没有fallthrough标注,
//可能是一个逻辑错误,在编译时编译器可能会给出警告,以提醒修改

case2:
func2();
//这里也缺少break语句,但是使用了fallthrough标注,
//说明是开发者有意为之的,编译器不会给出任何警告
[[fallthrough]];

case3:
func3();
}

注意:在 gcc/g++中, [[fallthrough]] 后面的分号不是必需的,在 Visual Studio 中必须加上分号,否则无法编译通过。

熟悉 Golang 的读者,可能对 fallthrough 这一语法特性非常熟悉, Golang 中在 switch-case 后加上 fallthrough,是一个常用的告诉编译器意图的语法规则。代码示例如下:

//以下是Golang语法
s:="abcd"
switchs[3]{
case'a':
fmt.Println("Theintegerwas<= 4")
fallthrough

case'b':
fmt.Println("Theintegerwas<= 5")
fallthrough

case'c':
fmt.Println("Theintegerwas<= 6")

default:
fmt.Println("defaultcase")
}

[[nodiscard]]一般用于修饰函数,告诉函数调用者必须关注该函数的返回值(即不能丢弃该函数的返回值)。如果函数调用者未将该函数的返回值赋值给一个变量,则编译器会给出一个警告。例如,假设有一个网络连接函数 connect,我们通过返回值明确说明了连接是否建立成功,则为了防止调用者在使用时直接将该值丢弃,我们可以将该函数使用[[nodiscard]]标记:

[[nodiscard]]intconnect(constchar*address,shortport)
{
//实现省略
}

intmain()
{
//忽略了connect函数的返回值,编译器会给出一个警告
connect("127.0.0.1",8888);
return0;
}

在 C++ 20 中,对于诸如 operator new()、 std::allocate()等库函数均使用了 [[nodiscard]] 进行标记,以强调必须使用这些函数的返回值。再来看另外一个标记。在通常情况下,编译器会对程序代码中未使用的函数或变量给出警告,另一些编译器干脆不允许通过编译。在 C++ 17 之前,程序员为了消除这些未使用的变量带来的编译警告或者错误,要么修改编译器的警告选项设置,要么定义一个类似于 UNREFERENCED_PARAMETER 的宏来显式调用这些未使用的变量一次,以消除编译警告或错误:

#defineUNREFERENCED_PARAMETER(x)x

intAPIENTRYwWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPWSTRlpCmdLine,intnCmdShow)
{
//C++17之前为了消除编译器对未使用的变量hPrevInstance、lpCmdLine给出的警告,我们可以这么做
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
//无关代码省略
}

以上代码节选自一个标准 Win32 程序的结构,其中的函数参数 hPrevInstance 和 lpCmdLine 一般不会被用到,编译器会给出警告。为了消除这类警告,这里定义了一个宏 UNREFERENCED_PARAMETER 并进行调用,造成这两个参数被使用的假象。C++17 有了 [[maybe_unused]] 注解之后,我们就再也不需要这类宏来“欺骗”编译器了。以上代码使用该注解后可以修改如下:

intAPIENTRYwWinMain(HINSTANCEhInstance,
[[maybe_unused]]HINSTANCEhPrevInstance,
[[maybe_unused]]LPWSTRlpCmdLine,
intnCmdShow)
{
//无关代码省略
}

案例 3:final、 override 关键字和 =default、 =delete 语法

3.1 final 关键字

在 C++11 之前,我们没有特别好的方法阻止一个类被其他类继承,到了 C++11 有了 final 关键字我们就可以做到了。final 关键字修饰一个类,这个类将不允许被继承,这在其他语言(如 Java)中早就实现了。在 C++ 11 中, final 关键字要写在类名的后面,这在其他语言中是写在 class 关键字前面的。示例如下:

classAfinal
{
};

classB:A
{
};

由于类 A 被声明成 final, B 继承 A, 所以编译器会报如下错误提示类 A 不能被继承:

errorC3246:'B':cannotinheritfrom'A'asithasbeendeclaredas'final'

3.2 override 关键字

C++98/03 语法规定,在父类中加了 virtual 关键字的方法可以被子类重写,子类重写该方法时可以加或不加 virtual 关键字,例如下面这样:

classA
{
protected:
virtualvoidfunc(inta,intb)
{
}
};

classB:A
{
protected:
virtualvoidfunc(inta,intb)
{
}
};

classC:B
{
protected:
voidfunc(inta,intb)
{
}
};

这种宽松的规定可能会带来以下两个问题。

  • 当我们阅读代码时,无论子类重写的方法是否添加了 virtual 关键字,我们都无法 直观地确定该方法是否是重写的父类方法。

  • 如果我们在子类中不小心写错了需要重写的方法的函数签名(可能是参数类型、 个数或返回值类型),这个方法就会变成一个独立的方法,这可能会违背我们重写 父类某个方法的初衷,而编译器在编译时并不会检查到这个错误。

为了解决以上两个问题, C++11 引进了 override 关键字,其实 override 关键字并不是新语法,在 Java 等其他编程语言中早就支持。类方法被 override 关键字修饰,表明该方法重写了父类的同名方法,加了该关键字后,编译器会在编译阶段做相应的检查,如果其父类不存在相同签名格式的类方法,编译器就会给出相应的错误提示。情形一,父类不存在,子类标记了 override 的方法:

classA
{
};

classB:A
{
protected:
voidfunc(intk,intd)override
{
}
};

由于在父类 A 中没有 func 方法,所以编译器会提示错误:

errorC3668:'B::func':methodwithoverridespecifier'override'didnotoverride
anybaseclassmethods

情形二,父类存在,子类标记了 override 的方法,但函数签名不一致:

classA
{
protected:
virtualintfunc(intk,intd)
{
return0;
}
};

classB:A
{
protected:
virtualvoidfunc(intk,intd)override
{
}
};

上述代码编译器会报同样的错误。正确的代码如下:

classA
{
protected:
virtualvoidfunc(intk,intd)
{
}
};

classB:A
{
protected:
virtualvoidfunc(intk,intd)override
{
}
};

3.3 default 语法

如果一个 C++类没有显式给出构造函数、析构函数、拷贝构造函数、 operator= 这几类函数的实现,则在需要它们时,编译器会自动生成;或者,在给出这些函数的声明时,如果没有给出其实现,则编译器在链接时会报错。如果使用=default 标记这类函数,则编译器会给出默认的实现。来看一个例子:

classA
{
};

intmain()
{
Aa;
return0;
}

这样的代码是可以编译通过的,因为编译器默认生成 A 的一个无参构造函数,假设我们现在向 A 提供一个有参构造函数:

classA
{
public:
A(inti)
{
}
};

intmain()
{
Aa;
return0;
}

这时,编译器就不会自动生成默认的无参构造函数了,这段代码会编译出错,提示 A 没有合适的无参构造函数:

errorC2512:'A':noappropriatedefaultconstructoravailable

我们这时可以手动为 A 加上无参构造函数, 也可以使用=default 语法强行让编译器自己生成:

classA
{
public:
A()=default;
A(inti)
{
}
};

intmain()
{
Aa;
return0;
}

=default 最大的作用可能是在开发中简化了构造函数中没有实际初始化代码的写法,尤其是声明和实现分别属于.h 和.cpp 文件。例如,对于类 A,其头文件为 a.h,其实现文件为 a.cpp,则正常情况下我们需要在 a.cpp 文件中写其构造函数和析构函数的实现(可能没有实际的构造和析构逻辑):

//a.h
classA
{
public:
A();
~A();
};

//a.cpp
#include"a.h"

A::A()
{
}

A::~A()
{
}

可以发现,即使在 A 的构造函数和析构函数中什么逻辑也没有,我们还是不得不在 a.cpp 中写上构造函数和析构函数的实现。有了=default 关键字,我们就可以在 a.h 中直接写成:

//a.h
classA
{
public:
A()=default;
~A()=default;
};

//a.cpp
#include"a.h"
//在cpp文件中就不用再写A的构造函数和析构函数的实现了

3.4 =delete 语法

既然有强制让编译器生成构造函数、析构函数、拷贝构造函数、 operator=的语法,那么也应该有禁止编译器生成这些函数的语法,没错,就是 =delete。在 C++ 98/03 规范中, 如果我们想让一个类不能被拷贝(即不能调用其拷贝构造函数),则可以将其拷贝构造函数和 operator=函数定义成 private 的:

classA
{
public:
A()=default;
~A()=default;

private:
A(constA&a)
{
}

A&operator=(constA&a)
{
}
};

intmain()
{
Aa1;
Aa2(a1);
Aa3;
a3=a1;
return0;
}

通过以上代码利用 a1 构造 a2 时,编译器会提示错误:

errorC2248:'A::A':cannotaccessprivatememberdeclaredinclass'A'
errorC2248:'A::operator=':cannotaccessprivatememberdeclaredinclass'A'

我们利用这种方式间接实现了一个类不能被拷贝的功能,这也是继承自 boost::noncopyable 的类不能被拷贝的实现原理。现在有了=delete语法,我们直接使用该语法禁止编译器生成这两个函数即可:

classA
{
public:
A()=default;
~A()=default;
public:
A(constA&a)=delete;
A&operator=(constA&a)=delete;
};

intmain()
{
Aa1;
//Aa2(a1);
Aa3;
//a3=a1;
return0;
}

一般在一些工具类中, 我们不需要用到构造函数、 析构函数、 拷贝构造函数、 operator= 这 4 个函数,为了防止编译器自己生成,同时为了减小生成的可执行文件的体积,建议使用=delete 语法禁止编译器为这 4 个函数生成默认的实现代码,例如:

//这是一个字符转码工具类
classEncodeUtil
{
public:
staticstd::wstringAnsiiToUnicode(conststd::string&strAnsii);
staticstd::stringUnicodeToAnsii(conststd::wstring&strUnicode);
staticstd::stringAnsiiToUtf8(conststd::string&strAnsii);
staticstd::stringUtf8ToAnsii(conststd::string&strUtf8);
staticstd::stringUnicodeToUtf8(conststd::wstring&strUnicode);
staticstd::wstringUtf8ToUnicode(conststd::string&strUtf8);

private:
EncodeUtil()=delete;
~EncodeUtil()=delete;
EncodeUtil(constEncodeUtil&rhs)=delete;
EncodeUtil&operator=(constEncodeUtil&rhs)=delete;
};

案例 4:对多线程的支持

我们来看一个稍微复杂一点的例子。在 C++11 之前,由于 C++98/03 本身缺乏对线程和线程同步原语的支持,我们要写一个生产者消费者逻辑要这么写。在 Windows 上:

/**
*RecvMsgTask.h
*/
classCRecvMsgTask:publicCThreadPoolTask
{
public:
CRecvMsgTask(void);
~CRecvMsgTask(void);

public:
virtualintRun();
virtualintStop();
virtualvoidTaskFinish();

BOOLAddMsgData(CBuffer*lpMsgData);

private:
BOOLHandleMsg(CBuffer*lpMsg);

private:
HANDLEm_hEvent;
CRITICAL_SECTIONm_csItem;
HANDLEm_hSemaphore;
std::vectorm_arrItem;
};

/**
*RecvMsgTask.cpp
*/
CRecvMsgTask::CRecvMsgTask(void)
{
::InitializeCriticalSection(&m_csItem);
m_hSemaphore=::CreateSemaphore(NULL,0,0x7FFFFFFF,NULL);
m_hEvent=::CreateEvent(NULL,TRUE,FALSE,NULL);
}

CRecvMsgTask::~CRecvMsgTask(void)
{
::DeleteCriticalSection(&m_csItem);

if(m_hSemaphore!=NULL)
{
::CloseHandle(m_hSemaphore);
m_hSemaphore=NULL;
}

if(m_hEvent!=NULL)
{
::CloseHandle(m_hEvent);
m_hEvent=NULL;
}
}

intCRecvMsgTask::Run()
{
HANDLEhWaitEvent[2];
DWORDdwIndex;
CBuffer*lpMsg;

hWaitEvent[0]=m_hEvent;
hWaitEvent[1]=m_hSemaphore;

while(1)
{
dwIndex=::WaitForMultipleObjects(2,hWaitEvent,FALSE,INFINITE);

if(dwIndex==WAIT_OBJECT_0)
break;

lpMsg=NULL;

::EnterCriticalSection(&m_csItem);
if(m_arrItem.size()>0)
{
//消费者从队列m_arrItem中取出任务执行
lpMsg=m_arrItem[0];
m_arrItem.erase(m_arrItem.begin()+0);
}
::LeaveCriticalSection(&m_csItem);

if(NULL==lpMsg)
continue;

//处理任务
HandleMsg(lpMsg);

deletelpMsg;
}

return0;
}

intCRecvMsgTask::Stop()
{
m_HttpClient.SetCancalEvent();
::SetEvent(m_hEvent);
return0;
}

voidCRecvMsgTask::TaskFinish()
{
}

//生产者调用这个方法将Task放入队列m_arrItem中
BOOLCRecvMsgTask::AddMsgData(CBuffer*lpMsgData)
{
if(NULL==lpMsgData)
returnFALSE;

::EnterCriticalSection(&m_csItem);
m_arrItem.push_back(lpMsgData);
::LeaveCriticalSection(&m_csItem);

::ReleaseSemaphore(m_hSemaphore,1,NULL);

returnTRUE;
}

Linux 下:

#include
#include
#include
#include
#include
#include

classTask
{
public:
Task(inttaskID)
{
this->taskID=taskID;
}

voiddoTask()
{
std::cout<< "handleatask,taskID:"<< taskID << ",threadID:"<< pthread_self() << std::endl; 
     }
     
 private:
     int taskID;
 };
 
 pthread_mutex_t  mymutex;
 std::listtasks;
pthread_cond_tmycv;

void*consumer_thread(void*param)
{
Task*pTask=NULL;
while(true)
{
pthread_mutex_lock(&mymutex);
while(tasks.empty())
{
//如果获得了互斥锁,但是条件不合适的话,pthread_cond_wait会释放锁,不往下执行。
//当发生变化后,条件合适,pthread_cond_wait将直接获得锁。
pthread_cond_wait(&mycv,&mymutex);
}

pTask=tasks.front();
tasks.pop_front();

pthread_mutex_unlock(&mymutex);

if(pTask==NULL)
continue;

pTask->doTask();
deletepTask;
pTask=NULL;
}

returnNULL;
}

void*producer_thread(void*param)
{
inttaskID=0;
Task*pTask=NULL;

while(true)
{
pTask=newTask(taskID);

pthread_mutex_lock(&mymutex);
tasks.push_back(pTask);
std::cout<< "produceatask,taskID:"<< taskID << ",threadID:"<< pthread_self() << std::endl; 
         
         pthread_mutex_unlock(&mymutex);
         
         //释放信号量,通知消费者线程
         pthread_cond_signal(&mycv);
         
         taskID ++;
 
         //休眠1秒
         sleep(1);
     }
     
     returnNULL;
}

intmain()
{
pthread_mutex_init(&mymutex,NULL);
pthread_cond_init(&mycv,NULL);

//创建5个消费者线程
pthread_tconsumerThreadID[5];
for(inti=0;i< 5; ++i)
         pthread_create(&consumerThreadID[i], NULL, consumer_thread, NULL);
     
     //创建一个生产者线程
     pthread_t producerThreadID;
     pthread_create(&producerThreadID, NULL, producer_thread, NULL);
 
     pthread_join(producerThreadID, NULL);
     
     for(inti=0;i< 5; ++i)
         pthread_join(consumerThreadID[i], NULL);
     
     pthread_cond_destroy(&mycv);
     pthread_mutex_destroy(&mymutex);
 
     return0;
}

怎么样?上述代码如果对于新手来说,望而却步。为了实现这样的功能在 Windows 上你需要掌握线程如何创建、线程同步对象 CriticalSection、Event、Semaphore、WaitForSingleObject/WaitForMultipleObjects 等操作系统对象和 API。

在 Linux 上需要掌握线程创建,你需要了解线程创建、互斥体、条件变量。对于需要支持多个平台的开发,需要开发者同时熟悉上述原理并编写多套适用不同平台的代码。C++11 的线程库改变了这个现状,现在你只需要掌握 std::thread、std::mutex、std::condition_variable 少数几个线程同步对象即可,同时使用这些对象编写出来的代码也可以跨平台。示例如下:

#include
#include
#include
#include
#include

classTask
{
public:
Task(inttaskID)
{
this->taskID=taskID;
}

voiddoTask()
{
std::cout<< "handleatask,taskID:"<< taskID << ",threadID:"<< std::get_id() << std::endl; 
     }
     
 private:
     int taskID;
 };
 
 std::mutex                mymutex;
 std::listtasks;
std::condition_variablemycv;

void*consumer_thread()
{
Task*pTask=NULL;
while(true)
{
std::unique_lockguard(mymutex);
while(tasks.empty())
{
//如果获得了互斥锁,但是条件不合适的话,pthread_cond_wait会释放锁,不往下执行。
//当发生变化后,条件合适,pthread_cond_wait将直接获得锁。
mycv.wait(guard);
}

pTask=tasks.front();
tasks.pop_front();

if(pTask==NULL)
continue;

pTask->doTask();
deletepTask;
pTask=NULL;
}

returnNULL;
}

void*producer_thread()
{
inttaskID=0;
Task*pTask=NULL;

while(true)
{
pTask=newTask(taskID);

//使用括号减小guard锁的作用范围
{
std::lock_guardguard(mymutex);
tasks.push_back(pTask);
std::cout<< "produceatask,taskID:"<< taskID << ",threadID:"<< std::get_id() << std::endl; 
         }
         
         //释放信号量,通知消费者线程
         mycv.notify_one();
         
         taskID ++;
 
         //休眠1秒
         std::seconds(1));
     }
     
     returnNULL;
}

intmain()
{
//创建5个消费者线程
std::threadconsumer1(consumer_thread);
std::threadconsumer2(consumer_thread);
std::threadconsumer3(consumer_thread);
std::threadconsumer4(consumer_thread);
std::threadconsumer5(consumer_thread);

//创建一个生产者线程
std::threadproducer(producer_thread);

producer.join();
consumer1.join();
consumer2.join();
consumer3.join();
consumer4.join();
consumer5.join();

return0;
}

感觉如何?代码既简洁又统一。这就是 C++11 之后使用 Modern C++ 开发的效率!C++11 之后的 C++ 更像一门新的语言。当 C++11 的编译器发布之后(Visual Studio 2013、g++4.8),我第一时间更新了我的编译器,同时把我们的项目使用了 C++11 特性进行了改造。当然,例子还有很多,限于文章篇幅,这里就列举 4 个案例。

当然,Modern C++ 已经成为业界开发的主流,你应该欢迎它、拥抱它、熟悉它、使用它。

编辑:jq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11229

    浏览量

    208927
  • API
    API
    +关注

    关注

    2

    文章

    1485

    浏览量

    61817
  • C++
    C++
    +关注

    关注

    22

    文章

    2104

    浏览量

    73494
  • 代码
    +关注

    关注

    30

    文章

    4748

    浏览量

    68351

原文标题:Modern C++ 有哪些能真正提升开发效率的语法糖?

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    OpenVINO2024 C++推理使用技巧

    很多人都使用OpenVINO新版的C++ 或者Python的SDK,都觉得非常好用,OpenVINO2022之后的版本C++ SDK做了大量的优化与整理,已经是非常贴近开发的使用习惯与推理方式。与OpenCV的Mat对象对接方式
    的头像 发表于 07-26 09:20 788次阅读

    C++中实现类似instanceof的方法

    C++多态与继承,但是很多人开始学习C++,有时候会面临一个常见问题,就是如何向下转型,特别是不知道具体类型的时候,这个时候就希望C++ 可以向Java或者Python中有insta
    的头像 发表于 07-18 10:16 534次阅读
    <b class='flag-5'>C++</b>中实现类似instanceof的方法

    鸿蒙OS开发实例:【Native C++

    使用DevEco Studio创建一个Native C++应用。应用采用Native C++模板,实现使用NAPI调用C标准库的功能。使用C标准库hypot接口计算两个给定数平方和的平
    的头像 发表于 04-14 11:43 2505次阅读
    鸿蒙OS<b class='flag-5'>开发</b>实例:【Native <b class='flag-5'>C++</b>】

    为什么很少用C++开发单片机

    C语言是面向过程的语言,C++是面向对象的编程语言。结合本文来说,面向过程相比面向对象的编程,生成代码量(bin文件)更小,运行效率更高。
    发表于 03-25 14:26 832次阅读
    为什么很少用<b class='flag-5'>C++</b><b class='flag-5'>开发</b>单片机

    请问CubeIDE如何支持C++开发

    CubeIDE如何支持C++开发。有没有一些例程。
    发表于 03-25 06:22

    c语言,c++,java,python区别

    C语言、C++、Java和Python是四种常见的编程语言,各有优点和特点。 C语言: C语言是一种面向过程的编程语言。它具有底层的特性,能够对计算机硬件进行直接操作。
    的头像 发表于 02-05 14:11 2237次阅读

    vb语言和c++语言的区别

    Microsoft开发的一种面向对象的事件驱动编程语言。它的设计目标是简化编程过程,让初学者也快速上手。与之相比,C++语言是一种通用的、面向对象的编程语言,其设计目标是提供高性能的系统级编程。
    的头像 发表于 02-01 10:20 2062次阅读

    C++在Linux内核开发中从争议到成熟

    Linux 内核邮件列表中一篇已有六年历史的老帖近日再次引发激烈讨论 —— 主题是建议将 Linux 内核的开发语言从 C 转换为更现代的 C++
    的头像 发表于 01-31 14:11 588次阅读
    <b class='flag-5'>C++</b>在Linux内核<b class='flag-5'>开发</b>中从争议到成熟

    C++简史:C++是如何开始的

    MISRA C++:2023,MISRA® C++ 标准的下一个版本,来了!为了帮助您做好准备,我们介绍了 Perforce 首席技术支持工程师 Frank van den Beuken 博士撰写
    的头像 发表于 01-11 09:00 536次阅读
    <b class='flag-5'>C++</b>简史:<b class='flag-5'>C++</b>是如何开始的

    如何提升单片机开发技术?

    单片机开发是现代电子技术中的重要分支,其在各个领域都有着广泛的应用。单片机开发技术的提升不仅可以提高工作效率,还可以提高工作质量和创新能力。那么,如何
    发表于 01-05 10:14

    基于C/C++面向对象的方式封装socket通信类

    在掌握了基于 TCP 的套接字通信流程之后,为了方便使用,提高编码效率,可以对通信操作进行封装,本着浅入深的原则,先基于 C 语言进行面向过程的函数封装,然后再基于 C++ 进行面向
    的头像 发表于 12-26 09:57 1284次阅读

    如何使用SystemC做RTL和C/C++的联合仿真呢?

    当FPGA开发者需要做RTL和C/C++联合仿真的时候,一些常用的方法包括使用MicroBlaze软核,或者使用QEMU仿真ZYNQ的PS部分。
    的头像 发表于 12-13 10:13 1202次阅读
    如何使用SystemC做RTL和<b class='flag-5'>C</b>/<b class='flag-5'>C++</b>的联合仿真呢?

    C语言和C++中那些不同的地方

    C语言虽说经常和C++在一起被大家提起,但可千万不要以为它们是一个东西。现在我们常用的C语言是C89标准,C++
    的头像 发表于 12-07 14:29 908次阅读
    <b class='flag-5'>C</b>语言和<b class='flag-5'>C++</b>中那些不同的地方

    开箱即用!教你如何正确使用华为云CodeArts IDE for C/C++

    C/C++编码体验、方便的访问华为云资源、简单的引用华为云服务于一身,实现C/C++开发者在个人研发作业体验和
    的头像 发表于 11-29 17:40 759次阅读
    开箱即用!教你如何正确使用华为云CodeArts IDE for <b class='flag-5'>C</b>/<b class='flag-5'>C++</b>!

    c++怎么开始编程

    C++是一种高级的、通用的编程语言,用于开发各种类型的应用程序。它是从C语言演变而来,也是一种静态类型语言,可以在不同的平台上进行开发C++
    的头像 发表于 11-27 15:56 894次阅读