Given the DagPath for any Mesh object, listing the Shading Groups connected to it is easy. The MFnMesh::getConnectedSetsAndMembers() will do the trick as shown below:

MStatus ListShadingGroups(const MDagPath& inputShapeDagPath)
{
if(inputShapeDagPath.hasFn(MFn::kMesh))
{
// Find the Shading Engines Connected to the SourceNode
MFnMesh fnMesh(inputShapeDagPath.node());

// A ShadingGroup will have a MFnSet
MObjectArray sets, comps;
fnMesh.getConnectedSetsAndMembers(inputShapeDagPath.instanceNumber(), sets, comps, true);

// Each set is a Shading Group. Loop through them
for(unsigned int i = 0; i < sets.length(); ++i)
{
MFnDependencyNode fnDepSGNode(sets[i]);

cout << fnDepSGNode.name() << endl;
}
}

return MStatus::kSuccess;
}

Subdivision surfaces can also be worked out similarily, using the MFnSubd::getConnectedSetsAndMembers().

However, for a NURBS surface, we have to do little bit more work with the use of Plugs. A Shading Group will be connected to the instObjGroups attribute of the shape. This instObjGroups is an array that is indexed by the instance number of the shape. Thus, the code to list the connected shading groups will look like below:

MStatus ListShadingGroups(const MDagPath& inputShapeDagPath)
{
if(inputShapeDagPath.hasFn(MFn::kNurbsSurface))
{
MFnNurbsSurface fnNurbs(inputShapeDagPath.node());

MObject instObjGroupsAttr = fnNurbs.attribute("instObjGroups");

MPlug instPlug(inputShapeDagPath.node(), instObjGroupsAttr);

// Get the instance that our node is referring to;
// In other words get the Plug for instObjGroups[intanceNumber];

MPlug instPlugElem = instPlug.elementByLogicalIndex(inputShapeDagPath.instanceNumber());

// Find the ShadingGroup plugs that we are connected to as Source
MPlugArray SGPlugArray;
instPlugElem.connectedTo(SGPlugArray, false, true);

// Loop through each ShadingGroup Plug
for(unsigned int i=0; i < SGPlugArray.length(); ++i)
{
MFnDependencyNode fnDepSGNode(SGPlugArray[i].node());

cout << fnDepSGNode.name() << endl;
}
}

return MStatus::kSuccess;
}